From 9d8f54af9d39cd37bab617c2ff5d76e0568850dd Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 24 Jun 2016 23:39:13 -0400 Subject: [PATCH] Custom ISettings implemtation for app group support on iOS. --- src/iOS/AppDelegate.cs | 20 ++- src/iOS/Services/Settings.cs | 235 +++++++++++++++++++++++++++++++++++ src/iOS/iOS.csproj | 15 ++- 3 files changed, 251 insertions(+), 19 deletions(-) create mode 100644 src/iOS/Services/Settings.cs diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index 10e70d00f..b742777a4 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -10,7 +10,6 @@ using Bit.App.Abstractions; using Bit.App.Services; using Microsoft.Practices.Unity; using Bit.iOS.Services; -using Plugin.Settings; using Plugin.Connectivity; using Acr.UserDialogs; using Bit.App.Repositories; @@ -26,19 +25,11 @@ using Plugin.DeviceInfo; namespace Bit.iOS { - // The UIApplicationDelegate for the application. This class is responsible for launching the - // User Interface of the application, as well as listening (and optionally responding) to - // application events from iOS. [Register("AppDelegate")] public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { - // - // This method is invoked when the application has loaded and is ready to run. In this - // method you should instantiate the window, load the UI into it and then make the window - // visible. - // - // You have 17 seconds to return from this method, or iOS will terminate your application. - // + public ISettings Settings { get; set; } + public override bool FinishedLaunching(UIApplication app, NSDictionary options) { CrossFingerprint.AllowReuse = false; @@ -87,7 +78,8 @@ namespace Bit.iOS UIApplication.SharedApplication.KeyWindow.EndEditing(true); // Log the date/time we last backgrounded - CrossSettings.Current.AddOrUpdateValue(Constants.SettingLastBackgroundedDate, DateTime.UtcNow); + + Settings.AddOrUpdateValue(Constants.SettingLastBackgroundedDate, DateTime.UtcNow); base.DidEnterBackground(uiApplication); Debug.WriteLine("DidEnterBackground"); @@ -198,11 +190,13 @@ namespace Bit.iOS .RegisterType(new ContainerControlledLifetimeManager()) // Other .RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager()) - .RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager()) .RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager()) .RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager()) .RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager()); + Settings = new Settings("group.com.8bit.bitwarden"); + container.RegisterInstance(Settings, new ContainerControlledLifetimeManager()); + CrossPushNotification.Initialize(container.Resolve()); container.RegisterInstance(CrossPushNotification.Current, new ContainerControlledLifetimeManager()); diff --git a/src/iOS/Services/Settings.cs b/src/iOS/Services/Settings.cs new file mode 100644 index 000000000..346ffc5b4 --- /dev/null +++ b/src/iOS/Services/Settings.cs @@ -0,0 +1,235 @@ + +using System; +#if __UNIFIED__ +using Foundation; +#else +using MonoTouch.Foundation; +#endif +using Plugin.Settings.Abstractions; + +namespace Bit.iOS.Services +{ + /// + /// Main implementation for ISettings + /// + public class Settings : ISettings + { + private readonly object locker = new object(); + private readonly NSUserDefaults _defaults; + + public Settings(string defaultsName) + { + _defaults = string.IsNullOrWhiteSpace(defaultsName) ? NSUserDefaults.StandardUserDefaults + : new NSUserDefaults(defaultsName, NSUserDefaultsType.SuiteName); + } + + /// + /// Gets the current value or the default that you specify. + /// + /// Vaue of t (bool, int, float, long, string) + /// Key for settings + /// default value if not set + /// Value or default + public T GetValueOrDefault(string key, T defaultValue = default(T)) + { + lock(locker) + { + var defaults = _defaults; + + if(defaults.ValueForKey(new NSString(key)) == null) + return defaultValue; + + Type typeOf = typeof(T); + if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + typeOf = Nullable.GetUnderlyingType(typeOf); + } + object value = null; + var typeCode = Type.GetTypeCode(typeOf); + switch(typeCode) + { + case TypeCode.Decimal: + var savedDecimal = defaults.StringForKey(key); + value = Convert.ToDecimal(savedDecimal, System.Globalization.CultureInfo.InvariantCulture); + break; + case TypeCode.Boolean: + value = defaults.BoolForKey(key); + break; + case TypeCode.Int64: + var savedInt64 = defaults.StringForKey(key); + value = Convert.ToInt64(savedInt64, System.Globalization.CultureInfo.InvariantCulture); + break; + case TypeCode.Double: + value = defaults.DoubleForKey(key); + break; + case TypeCode.String: + value = defaults.StringForKey(key); + break; + case TypeCode.Int32: +#if __UNIFIED__ + value = (Int32)defaults.IntForKey(key); +#else + value = defaults.IntForKey(key); +#endif + break; + case TypeCode.Single: +#if __UNIFIED__ + value = (float)defaults.FloatForKey(key); +#else + value = defaults.FloatForKey(key); +#endif + break; + + case TypeCode.DateTime: + var savedTime = defaults.StringForKey(key); + if(string.IsNullOrWhiteSpace(savedTime)) + { + value = defaultValue; + } + else + { + var ticks = Convert.ToInt64(savedTime, System.Globalization.CultureInfo.InvariantCulture); + if(ticks >= 0) + { + //Old value, stored before update to UTC values + value = new DateTime(ticks); + } + else + { + //New value, UTC + value = new DateTime(-ticks, DateTimeKind.Utc); + } + } + break; + default: + + if(defaultValue is Guid) + { + var outGuid = Guid.Empty; + var savedGuid = defaults.StringForKey(key); + if(string.IsNullOrWhiteSpace(savedGuid)) + { + value = outGuid; + } + else + { + Guid.TryParse(savedGuid, out outGuid); + value = outGuid; + } + } + else + { + throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name)); + } + + break; + } + + + return null != value ? (T)value : defaultValue; + } + } + + /// + /// Adds or updates a value + /// + /// key to update + /// value to set + /// True if added or update and you need to save + public bool AddOrUpdateValue(string key, T value) + { + Type typeOf = typeof(T); + if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + typeOf = Nullable.GetUnderlyingType(typeOf); + } + var typeCode = Type.GetTypeCode(typeOf); + return AddOrUpdateValue(key, value, typeCode); + } + + private bool AddOrUpdateValue(string key, object value, TypeCode typeCode) + { + lock(locker) + { + var defaults = _defaults; + switch(typeCode) + { + case TypeCode.Decimal: + defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key); + break; + case TypeCode.Boolean: + defaults.SetBool(Convert.ToBoolean(value), key); + break; + case TypeCode.Int64: + defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key); + break; + case TypeCode.Double: + defaults.SetDouble(Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture), key); + break; + case TypeCode.String: + defaults.SetString(Convert.ToString(value), key); + break; + case TypeCode.Int32: + defaults.SetInt(Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture), key); + break; + case TypeCode.Single: + defaults.SetFloat(Convert.ToSingle(value, System.Globalization.CultureInfo.InvariantCulture), key); + break; + case TypeCode.DateTime: + defaults.SetString(Convert.ToString(-(Convert.ToDateTime(value)).ToUniversalTime().Ticks), key); + break; + default: + if(value is Guid) + { + if(value == null) + value = Guid.Empty; + + defaults.SetString(((Guid)value).ToString(), key); + } + else + { + throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name)); + } + break; + } + try + { + defaults.Synchronize(); + } + catch(Exception ex) + { + Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message); + } + } + + + return true; + } + + /// + /// Removes a desired key from the settings + /// + /// Key for setting + public void Remove(string key) + { + lock(locker) + { + var defaults = _defaults; + try + { + var nsString = new NSString(key); + if(defaults.ValueForKey(nsString) != null) + { + defaults.RemoveObject(key); + defaults.Synchronize(); + } + } + catch(Exception ex) + { + Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message); + } + } + } + } + +} diff --git a/src/iOS/iOS.csproj b/src/iOS/iOS.csproj index 5da683692..ba1d8d457 100644 --- a/src/iOS/iOS.csproj +++ b/src/iOS/iOS.csproj @@ -113,6 +113,7 @@ + @@ -133,6 +134,14 @@ + + ..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.dll + True + + + ..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll + True + @@ -173,12 +182,6 @@ ..\..\packages\Plugin.Fingerprint.1.1.1-beta\lib\Xamarin.iOS10\Plugin.Fingerprint.Abstractions.dll - - ..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.dll - - - ..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll - ..\..\packages\Splat.1.6.2\lib\Xamarin.iOS10\Splat.dll