Skip to content

Commit ac7f924

Browse files
rmarinhoCopilot
andauthored
Fix NullReferenceException and crash in ManifestExtensions (#182)
* Fix NullReferenceException and crash in ManifestExtensions - GetWKAppBundleIdentifier: handle null from GetNSExtensionAttributes. - SetWKAppBundleIdentifier: auto-create NSExtension/NSExtensionAttributes structure when setting a non-empty value (avoid silent no-op). - ParseDeviceFamilyFromNumber → TryParseDeviceFamilyFromNumber: unknown device family numbers are now logged and skipped (not coerced to IPhone), consistent with how the string parser already handles unknown values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix misleading log message in TryParseDeviceFamilyFromNumber Changed 'Skipped unknown device family' to 'Ignoring unrecognized device family number' to make it clear the value is not mapped to any family and is simply discarded. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4bfa9e8 commit ac7f924

2 files changed

Lines changed: 144 additions & 15 deletions

File tree

Xamarin.MacDev/ManifestExtensions.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -218,21 +218,28 @@ public static IPhoneDeviceType GetUIDeviceFamily (this PDictionary dict)
218218
return GetUIDeviceFamily (dict, ManifestKeys.UIDeviceFamily);
219219
}
220220

221-
static AppleDeviceFamily ParseDeviceFamilyFromNumber (PNumber number)
221+
static bool TryParseDeviceFamilyFromNumber (PNumber number, out AppleDeviceFamily family)
222222
{
223223
switch (number.Value) {
224224
case 1:
225-
return AppleDeviceFamily.IPhone;
225+
family = AppleDeviceFamily.IPhone;
226+
return true;
226227
case 2:
227-
return AppleDeviceFamily.IPad;
228+
family = AppleDeviceFamily.IPad;
229+
return true;
228230
case 3:
229-
return AppleDeviceFamily.TV;
231+
family = AppleDeviceFamily.TV;
232+
return true;
230233
case 4:
231-
return AppleDeviceFamily.Watch;
234+
family = AppleDeviceFamily.Watch;
235+
return true;
232236
case 6:
233-
return AppleDeviceFamily.MacCatalystOptimizedForMac;
237+
family = AppleDeviceFamily.MacCatalystOptimizedForMac;
238+
return true;
234239
default:
235-
throw new ArgumentOutOfRangeException (string.Format ("Unknown device family: {0}", number.Value));
240+
LoggingService.LogWarning ($"Ignoring unrecognized device family number: {number.Value}");
241+
family = default;
242+
return false;
236243
}
237244
}
238245

@@ -278,11 +285,12 @@ public static IPhoneDeviceType GetUIDeviceFamily (this PDictionary dict, string
278285
val |= ParseDeviceTypeFromString (p);
279286

280287
var number = element as PNumber;
281-
if (number != null)
282-
val |= ParseDeviceFamilyFromNumber (number).ToDeviceType ();
288+
if (number != null && TryParseDeviceFamilyFromNumber (number, out var family))
289+
val |= family.ToDeviceType ();
283290
}
284291
} else if (value is PNumber) {
285-
val |= ParseDeviceFamilyFromNumber ((PNumber) value).ToDeviceType ();
292+
if (TryParseDeviceFamilyFromNumber ((PNumber) value, out var family))
293+
val |= family.ToDeviceType ();
286294
} else if (value is PString) {
287295
val |= ParseDeviceTypeFromString ((PString) value);
288296
}
@@ -496,6 +504,23 @@ static PDictionary GetNSExtensionAttributes (this PDictionary dict)
496504
return extAtt;
497505
}
498506

507+
static PDictionary GetOrCreateNSExtensionAttributes (this PDictionary dict)
508+
{
509+
var ext = dict.Get<PDictionary> ("NSExtension");
510+
if (ext == null) {
511+
ext = new PDictionary ();
512+
dict ["NSExtension"] = ext;
513+
}
514+
515+
var extAtt = ext.Get<PDictionary> ("NSExtensionAttributes");
516+
if (extAtt == null) {
517+
extAtt = new PDictionary ();
518+
ext ["NSExtensionAttributes"] = extAtt;
519+
}
520+
521+
return extAtt;
522+
}
523+
499524
#endregion
500525

501526
#region Watch App Manifest Keys
@@ -513,19 +538,23 @@ public static void SetWKWatchKitApp (this PDictionary dict, bool value)
513538
public static string GetWKAppBundleIdentifier (this PDictionary dict)
514539
{
515540
var extAtt = GetNSExtensionAttributes (dict);
541+
if (extAtt == null)
542+
return null;
516543

517544
var str = extAtt.Get<PString> (ManifestKeys.WKAppBundleIdentifier);
518545
return str == null ? null : str.Value;
519546
}
520547

521548
public static void SetWKAppBundleIdentifier (this PDictionary dict, string value)
522549
{
523-
var extAtt = GetNSExtensionAttributes (dict);
524-
525-
if (string.IsNullOrEmpty (value))
526-
extAtt.Remove (ManifestKeys.WKAppBundleIdentifier);
527-
else
550+
if (string.IsNullOrEmpty (value)) {
551+
var extAtt = GetNSExtensionAttributes (dict);
552+
if (extAtt != null)
553+
extAtt.Remove (ManifestKeys.WKAppBundleIdentifier);
554+
} else {
555+
var extAtt = GetOrCreateNSExtensionAttributes (dict);
528556
extAtt [ManifestKeys.WKAppBundleIdentifier] = value;
557+
}
529558
}
530559

531560
public static string GetWKCompanionAppBundleIdentifier (this PDictionary dict)

tests/ManifestExtensionsTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#nullable enable
5+
6+
using NUnit.Framework;
7+
using Xamarin.MacDev;
8+
9+
namespace tests {
10+
11+
[TestFixture]
12+
public class ManifestExtensionsTests {
13+
14+
[Test]
15+
public void GetWKAppBundleIdentifier_ReturnsNull_WhenNSExtensionMissing ()
16+
{
17+
var dict = new PDictionary ();
18+
var result = dict.GetWKAppBundleIdentifier ();
19+
Assert.That (result, Is.Null);
20+
}
21+
22+
[Test]
23+
public void GetWKAppBundleIdentifier_ReturnsNull_WhenNSExtensionAttributesMissing ()
24+
{
25+
var dict = new PDictionary ();
26+
dict.Add ("NSExtension", new PDictionary ());
27+
var result = dict.GetWKAppBundleIdentifier ();
28+
Assert.That (result, Is.Null);
29+
}
30+
31+
[Test]
32+
public void GetWKAppBundleIdentifier_ReturnsValue_WhenPresent ()
33+
{
34+
var dict = new PDictionary ();
35+
var ext = new PDictionary ();
36+
var extAttr = new PDictionary ();
37+
extAttr.Add ("WKAppBundleIdentifier", new PString ("com.test.watchapp"));
38+
ext.Add ("NSExtensionAttributes", extAttr);
39+
dict.Add ("NSExtension", ext);
40+
41+
var result = dict.GetWKAppBundleIdentifier ();
42+
Assert.That (result, Is.EqualTo ("com.test.watchapp"));
43+
}
44+
45+
[Test]
46+
public void SetWKAppBundleIdentifier_CreatesStructure_WhenNSExtensionMissing ()
47+
{
48+
var dict = new PDictionary ();
49+
dict.SetWKAppBundleIdentifier ("com.test.app");
50+
Assert.That (dict.GetWKAppBundleIdentifier (), Is.EqualTo ("com.test.app"));
51+
}
52+
53+
[Test]
54+
public void SetWKAppBundleIdentifier_RemoveIsNoOp_WhenNSExtensionMissing ()
55+
{
56+
var dict = new PDictionary ();
57+
Assert.DoesNotThrow (() => dict.SetWKAppBundleIdentifier (""));
58+
}
59+
60+
[Test]
61+
public void GetUIDeviceFamily_SkipsUnknownDeviceFamilyNumber ()
62+
{
63+
var dict = new PDictionary ();
64+
var arr = new PArray ();
65+
arr.Add (new PNumber (99));
66+
dict.Add ("UIDeviceFamily", arr);
67+
68+
var result = dict.GetUIDeviceFamily ("UIDeviceFamily");
69+
Assert.That (result, Is.EqualTo (IPhoneDeviceType.NotSet));
70+
}
71+
72+
[Test]
73+
public void GetUIDeviceFamily_MixedKnownAndUnknown_SkipsUnknown ()
74+
{
75+
var dict = new PDictionary ();
76+
var arr = new PArray ();
77+
arr.Add (new PNumber (2)); // IPad
78+
arr.Add (new PNumber (99)); // unknown — should be skipped
79+
dict.Add ("UIDeviceFamily", arr);
80+
81+
var result = dict.GetUIDeviceFamily ("UIDeviceFamily");
82+
Assert.That (result.HasFlag (IPhoneDeviceType.IPad), Is.True);
83+
Assert.That (result.HasFlag (IPhoneDeviceType.IPhone), Is.False);
84+
}
85+
86+
[Test]
87+
public void GetUIDeviceFamily_ParsesKnownDeviceFamilies ()
88+
{
89+
var dict = new PDictionary ();
90+
var arr = new PArray ();
91+
arr.Add (new PNumber (1));
92+
arr.Add (new PNumber (2));
93+
dict.Add ("UIDeviceFamily", arr);
94+
95+
var result = dict.GetUIDeviceFamily ("UIDeviceFamily");
96+
Assert.That (result.HasFlag (IPhoneDeviceType.IPhone), Is.True);
97+
Assert.That (result.HasFlag (IPhoneDeviceType.IPad), Is.True);
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)