using System; using Foundation; #if __IOS__ using UIKit; #endif using Plugin.Settings.Abstractions; namespace Bit.iOS.Core.Services { /// /// Main implementation for ISettings /// [Preserve(AllMembers = true)] public class Settings : ISettings { private readonly object locker = new object(); private readonly string _defaultsName; public Settings(string defaultsName) { _defaultsName = defaultsName; } /// /// 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 /// Name of file for settings to be stored and retrieved /// Value or default T GetValueOrDefaultInternal(string key, T defaultValue = default(T), string fileName = null) { lock(locker) { var defaults = GetUserDefaults(fileName); if(defaults[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: value = (Int32)defaults.IntForKey(key); break; case TypeCode.Single: value = defaults.FloatForKey(key); 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($"Value of type {typeCode} is not supported."); } break; } return null != value ? (T)value : defaultValue; } } /// /// Adds or updates a value /// /// key to update /// value to set /// Name of file for settings to be stored and retrieved /// True if added or update and you need to save bool AddOrUpdateValueInternal(string key, T value, string fileName = null) { if(value == null) { Remove(key, fileName); return true; } Type typeOf = typeof(T); if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>)) { typeOf = Nullable.GetUnderlyingType(typeOf); } var typeCode = Type.GetTypeCode(typeOf); return AddOrUpdateValueCore(key, value, typeCode, fileName); } bool AddOrUpdateValueCore(string key, object value, TypeCode typeCode, string fileName) { lock(locker) { var defaults = GetUserDefaults(fileName); 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($"Value of type {typeCode} is not supported."); } 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 /// Name of file for settings to be stored and retrieved public void Remove(string key, string fileName = null) { lock(locker) { var defaults = GetUserDefaults(fileName); try { if(defaults[key] != null) { defaults.RemoveObject(key); defaults.Synchronize(); } } catch(Exception ex) { Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message); } } } /// /// Clear all keys from settings /// /// Name of file for settings to be stored and retrieved public void Clear(string fileName = null) { lock(locker) { var defaults = GetUserDefaults(fileName); try { var items = defaults.ToDictionary(); foreach(var item in items.Keys) { if(item is NSString nsString) defaults.RemoveObject(nsString); } defaults.Synchronize(); } catch(Exception ex) { Console.WriteLine("Unable to clear all defaults. Message: " + ex.Message); } } } /// /// Checks to see if the key has been added. /// /// Key to check /// Name of file for settings to be stored and retrieved /// True if contains key, else false public bool Contains(string key, string fileName = null) { lock(locker) { var defaults = GetUserDefaults(fileName); try { var setting = defaults[key]; return setting != null; } catch(Exception ex) { Console.WriteLine("Unable to clear all defaults. Message: " + ex.Message); } return false; } } NSUserDefaults GetUserDefaults(string fileName = null) { if(string.IsNullOrWhiteSpace(fileName) && !string.IsNullOrWhiteSpace(_defaultsName)) { return new NSUserDefaults(_defaultsName, NSUserDefaultsType.SuiteName); } return string.IsNullOrWhiteSpace(fileName) ? NSUserDefaults.StandardUserDefaults : new NSUserDefaults(fileName, NSUserDefaultsType.SuiteName); } #region GetValueOrDefault /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public decimal GetValueOrDefault(string key, decimal defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public bool GetValueOrDefault(string key, bool defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public long GetValueOrDefault(string key, long defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public string GetValueOrDefault(string key, string defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public int GetValueOrDefault(string key, int defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public float GetValueOrDefault(string key, float defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public DateTime GetValueOrDefault(string key, DateTime defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public Guid GetValueOrDefault(string key, Guid defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); /// /// Gets the current value or the default that you specify. /// /// Key for settings /// default value if not set /// Name of file for settings to be stored and retrieved /// Value or default public double GetValueOrDefault(string key, double defaultValue, string fileName = null) => GetValueOrDefaultInternal(key, defaultValue, fileName); #endregion #region AddOrUpdateValue /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, decimal value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, bool value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, long value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, string value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, int value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, float value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, DateTime value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, Guid value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); /// /// Adds or updates the value /// /// Key for settting /// Value to set /// Name of file for settings to be stored and retrieved /// True of was added or updated and you need to save it. public bool AddOrUpdateValue(string key, double value, string fileName = null) => AddOrUpdateValueInternal(key, value, fileName); #endregion /// /// Attempts to open the app settings page. /// /// true if success, else false and not supported public bool OpenAppSettings() { #if __IOS__ //Opening settings only open in iOS 8+ if(!UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) return false; try { UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString)); return true; } catch { return false; } #else return false; #endif } } }