diff --git a/Xamarin.MacDev/ManifestExtensions.cs b/Xamarin.MacDev/ManifestExtensions.cs index c8eae8b..9948d01 100644 --- a/Xamarin.MacDev/ManifestExtensions.cs +++ b/Xamarin.MacDev/ManifestExtensions.cs @@ -218,21 +218,28 @@ public static IPhoneDeviceType GetUIDeviceFamily (this PDictionary dict) return GetUIDeviceFamily (dict, ManifestKeys.UIDeviceFamily); } - static AppleDeviceFamily ParseDeviceFamilyFromNumber (PNumber number) + static bool TryParseDeviceFamilyFromNumber (PNumber number, out AppleDeviceFamily family) { switch (number.Value) { case 1: - return AppleDeviceFamily.IPhone; + family = AppleDeviceFamily.IPhone; + return true; case 2: - return AppleDeviceFamily.IPad; + family = AppleDeviceFamily.IPad; + return true; case 3: - return AppleDeviceFamily.TV; + family = AppleDeviceFamily.TV; + return true; case 4: - return AppleDeviceFamily.Watch; + family = AppleDeviceFamily.Watch; + return true; case 6: - return AppleDeviceFamily.MacCatalystOptimizedForMac; + family = AppleDeviceFamily.MacCatalystOptimizedForMac; + return true; default: - throw new ArgumentOutOfRangeException (string.Format ("Unknown device family: {0}", number.Value)); + LoggingService.LogWarning ($"Ignoring unrecognized device family number: {number.Value}"); + family = default; + return false; } } @@ -278,11 +285,12 @@ public static IPhoneDeviceType GetUIDeviceFamily (this PDictionary dict, string val |= ParseDeviceTypeFromString (p); var number = element as PNumber; - if (number != null) - val |= ParseDeviceFamilyFromNumber (number).ToDeviceType (); + if (number != null && TryParseDeviceFamilyFromNumber (number, out var family)) + val |= family.ToDeviceType (); } } else if (value is PNumber) { - val |= ParseDeviceFamilyFromNumber ((PNumber) value).ToDeviceType (); + if (TryParseDeviceFamilyFromNumber ((PNumber) value, out var family)) + val |= family.ToDeviceType (); } else if (value is PString) { val |= ParseDeviceTypeFromString ((PString) value); } @@ -496,6 +504,23 @@ static PDictionary GetNSExtensionAttributes (this PDictionary dict) return extAtt; } + static PDictionary GetOrCreateNSExtensionAttributes (this PDictionary dict) + { + var ext = dict.Get ("NSExtension"); + if (ext == null) { + ext = new PDictionary (); + dict ["NSExtension"] = ext; + } + + var extAtt = ext.Get ("NSExtensionAttributes"); + if (extAtt == null) { + extAtt = new PDictionary (); + ext ["NSExtensionAttributes"] = extAtt; + } + + return extAtt; + } + #endregion #region Watch App Manifest Keys @@ -513,6 +538,8 @@ public static void SetWKWatchKitApp (this PDictionary dict, bool value) public static string GetWKAppBundleIdentifier (this PDictionary dict) { var extAtt = GetNSExtensionAttributes (dict); + if (extAtt == null) + return null; var str = extAtt.Get (ManifestKeys.WKAppBundleIdentifier); return str == null ? null : str.Value; @@ -520,12 +547,14 @@ public static string GetWKAppBundleIdentifier (this PDictionary dict) public static void SetWKAppBundleIdentifier (this PDictionary dict, string value) { - var extAtt = GetNSExtensionAttributes (dict); - - if (string.IsNullOrEmpty (value)) - extAtt.Remove (ManifestKeys.WKAppBundleIdentifier); - else + if (string.IsNullOrEmpty (value)) { + var extAtt = GetNSExtensionAttributes (dict); + if (extAtt != null) + extAtt.Remove (ManifestKeys.WKAppBundleIdentifier); + } else { + var extAtt = GetOrCreateNSExtensionAttributes (dict); extAtt [ManifestKeys.WKAppBundleIdentifier] = value; + } } public static string GetWKCompanionAppBundleIdentifier (this PDictionary dict) diff --git a/tests/ManifestExtensionsTests.cs b/tests/ManifestExtensionsTests.cs new file mode 100644 index 0000000..3e7c734 --- /dev/null +++ b/tests/ManifestExtensionsTests.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using NUnit.Framework; +using Xamarin.MacDev; + +namespace tests { + + [TestFixture] + public class ManifestExtensionsTests { + + [Test] + public void GetWKAppBundleIdentifier_ReturnsNull_WhenNSExtensionMissing () + { + var dict = new PDictionary (); + var result = dict.GetWKAppBundleIdentifier (); + Assert.That (result, Is.Null); + } + + [Test] + public void GetWKAppBundleIdentifier_ReturnsNull_WhenNSExtensionAttributesMissing () + { + var dict = new PDictionary (); + dict.Add ("NSExtension", new PDictionary ()); + var result = dict.GetWKAppBundleIdentifier (); + Assert.That (result, Is.Null); + } + + [Test] + public void GetWKAppBundleIdentifier_ReturnsValue_WhenPresent () + { + var dict = new PDictionary (); + var ext = new PDictionary (); + var extAttr = new PDictionary (); + extAttr.Add ("WKAppBundleIdentifier", new PString ("com.test.watchapp")); + ext.Add ("NSExtensionAttributes", extAttr); + dict.Add ("NSExtension", ext); + + var result = dict.GetWKAppBundleIdentifier (); + Assert.That (result, Is.EqualTo ("com.test.watchapp")); + } + + [Test] + public void SetWKAppBundleIdentifier_CreatesStructure_WhenNSExtensionMissing () + { + var dict = new PDictionary (); + dict.SetWKAppBundleIdentifier ("com.test.app"); + Assert.That (dict.GetWKAppBundleIdentifier (), Is.EqualTo ("com.test.app")); + } + + [Test] + public void SetWKAppBundleIdentifier_RemoveIsNoOp_WhenNSExtensionMissing () + { + var dict = new PDictionary (); + Assert.DoesNotThrow (() => dict.SetWKAppBundleIdentifier ("")); + } + + [Test] + public void GetUIDeviceFamily_SkipsUnknownDeviceFamilyNumber () + { + var dict = new PDictionary (); + var arr = new PArray (); + arr.Add (new PNumber (99)); + dict.Add ("UIDeviceFamily", arr); + + var result = dict.GetUIDeviceFamily ("UIDeviceFamily"); + Assert.That (result, Is.EqualTo (IPhoneDeviceType.NotSet)); + } + + [Test] + public void GetUIDeviceFamily_MixedKnownAndUnknown_SkipsUnknown () + { + var dict = new PDictionary (); + var arr = new PArray (); + arr.Add (new PNumber (2)); // IPad + arr.Add (new PNumber (99)); // unknown — should be skipped + dict.Add ("UIDeviceFamily", arr); + + var result = dict.GetUIDeviceFamily ("UIDeviceFamily"); + Assert.That (result.HasFlag (IPhoneDeviceType.IPad), Is.True); + Assert.That (result.HasFlag (IPhoneDeviceType.IPhone), Is.False); + } + + [Test] + public void GetUIDeviceFamily_ParsesKnownDeviceFamilies () + { + var dict = new PDictionary (); + var arr = new PArray (); + arr.Add (new PNumber (1)); + arr.Add (new PNumber (2)); + dict.Add ("UIDeviceFamily", arr); + + var result = dict.GetUIDeviceFamily ("UIDeviceFamily"); + Assert.That (result.HasFlag (IPhoneDeviceType.IPhone), Is.True); + Assert.That (result.HasFlag (IPhoneDeviceType.IPad), Is.True); + } + } +}