Replaced Plugin.Settings with unit testable SettingsService (mobile app)pull/472/head
@ -0,0 +1,10 @@ | |||
namespace eShopOnContainers.Core.Services.Dependency | |||
{ | |||
public class DependencyService : IDependencyService | |||
{ | |||
public T Get<T>() where T : class | |||
{ | |||
return Xamarin.Forms.DependencyService.Get<T>(); | |||
} | |||
} | |||
} |
@ -0,0 +1,7 @@ | |||
namespace eShopOnContainers.Core.Services.Dependency | |||
{ | |||
public interface IDependencyService | |||
{ | |||
T Get<T>() where T : class; | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
using System.Collections.Generic; | |||
using eShopOnContainers.Core.Models.Basket; | |||
using eShopOnContainers.Core.Models.Catalog; | |||
using eShopOnContainers.Core.Models.Marketing; | |||
namespace eShopOnContainers.Core.Services.FixUri | |||
{ | |||
public interface IFixUriService | |||
{ | |||
void FixCatalogItemPictureUri(IEnumerable<CatalogItem> catalogItems); | |||
void FixBasketItemPictureUri(IEnumerable<BasketItem> basketItems); | |||
void FixCampaignItemPictureUri(IEnumerable<CampaignItem> campaignItems); | |||
} | |||
} |
@ -1,10 +1,9 @@ | |||
namespace eShopOnContainers.Core.Services.Location | |||
{ | |||
using System.Threading.Tasks; | |||
using Models.Location; | |||
using System.Threading.Tasks; | |||
namespace eShopOnContainers.Core.Services.Location | |||
{ | |||
public interface ILocationService | |||
{ | |||
Task UpdateUserLocation(Location newLocReq, string token); | |||
Task UpdateUserLocation(eShopOnContainers.Core.Models.Location.Location newLocReq, string token); | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
namespace eShopOnContainers.Core.Services.Settings | |||
{ | |||
public interface ISettingsService | |||
{ | |||
string AuthAccessToken { get; set; } | |||
string AuthIdToken { get; set; } | |||
bool UseMocks { get; set; } | |||
string UrlBase { get; set; } | |||
bool UseFakeLocation { get; set; } | |||
string Latitude { get; set; } | |||
string Longitude { get; set; } | |||
bool AllowGpsLocation { get; set; } | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
namespace eShopOnContainers.Core.Services.Settings | |||
{ | |||
public interface ISettingsServiceImplementation | |||
{ | |||
bool GetValueOrDefault(string key, bool defaultValue); | |||
string GetValueOrDefault(string key, string defaultValue); | |||
bool AddOrUpdateValue(string key, bool value); | |||
bool AddOrUpdateValue(string key, string value); | |||
void Remove(string key); | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
/* | |||
// Helpers/Settings.cs This file was automatically added when you installed the Settings Plugin. If you are not using a PCL then comment this file back in to use it. | |||
using Plugin.Settings; | |||
using Plugin.Settings.Abstractions; | |||
namespace eShopOnContainers.Droid.Helpers | |||
{ | |||
/// <summary> | |||
/// This is the Settings static class that can be used in your Core solution or in any | |||
/// of your client applications. All settings are laid out the same exact way with getters | |||
/// and setters. | |||
/// </summary> | |||
public static class Settings | |||
{ | |||
private static ISettings AppSettings | |||
{ | |||
get | |||
{ | |||
return CrossSettings.Current; | |||
} | |||
} | |||
#region Setting Constants | |||
private const string SettingsKey = "settings_key"; | |||
private static readonly string SettingsDefault = string.Empty; | |||
#endregion | |||
public static string GeneralSettings | |||
{ | |||
get | |||
{ | |||
return AppSettings.GetValueOrDefault(SettingsKey, SettingsDefault); | |||
} | |||
set | |||
{ | |||
AppSettings.AddOrUpdateValue(SettingsKey, value); | |||
} | |||
} | |||
} | |||
}*/ |
@ -0,0 +1,149 @@ | |||
using Android.App; | |||
using Android.Content; | |||
using Android.Preferences; | |||
using eShopOnContainers.Core.Services.Settings; | |||
using eShopOnContainers.Droid.Services; | |||
using System; | |||
[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] | |||
namespace eShopOnContainers.Droid.Services | |||
{ | |||
public class SettingsServiceImplementation : ISettingsServiceImplementation | |||
{ | |||
#region Internal Implementation | |||
readonly object _locker = new object(); | |||
ISharedPreferences GetSharedPreference() | |||
{ | |||
return PreferenceManager.GetDefaultSharedPreferences(Application.Context); | |||
} | |||
bool AddOrUpdateValueInternal<T>(string key, T value) | |||
{ | |||
if (Application.Context == null) | |||
return false; | |||
if (value == null) | |||
{ | |||
Remove(key); | |||
return true; | |||
} | |||
var type = typeof(T); | |||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) | |||
{ | |||
type = Nullable.GetUnderlyingType(type); | |||
} | |||
var typeCode = Type.GetTypeCode(type); | |||
lock (_locker) | |||
{ | |||
using (var sharedPrefs = GetSharedPreference()) | |||
{ | |||
using (var editor = sharedPrefs.Edit()) | |||
{ | |||
switch (typeCode) | |||
{ | |||
case TypeCode.Boolean: | |||
editor.PutBoolean(key, Convert.ToBoolean(value)); | |||
break; | |||
case TypeCode.String: | |||
editor.PutString(key, Convert.ToString(value)); | |||
break; | |||
default: | |||
throw new ArgumentException($"Value of type {typeCode} is not supported."); | |||
} | |||
editor.Commit(); | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T)) | |||
{ | |||
if (Application.Context == null) | |||
return defaultValue; | |||
if (!Contains(key)) | |||
return defaultValue; | |||
lock (_locker) | |||
{ | |||
using (var sharedPrefs = GetSharedPreference()) | |||
{ | |||
var type = typeof(T); | |||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) | |||
{ | |||
type = Nullable.GetUnderlyingType(type); | |||
} | |||
object value = null; | |||
var typeCode = Type.GetTypeCode(type); | |||
switch (typeCode) | |||
{ | |||
case TypeCode.Boolean: | |||
value = sharedPrefs.GetBoolean(key, Convert.ToBoolean(defaultValue)); | |||
break; | |||
case TypeCode.String: | |||
value = sharedPrefs.GetString(key, Convert.ToString(defaultValue)); | |||
break; | |||
default: | |||
throw new ArgumentException($"Value of type {typeCode} is not supported."); | |||
} | |||
return null != value ? (T)value : defaultValue; | |||
} | |||
} | |||
} | |||
bool Contains(string key) | |||
{ | |||
if (Application.Context == null) | |||
return false; | |||
lock (_locker) | |||
{ | |||
using (var sharedPrefs = GetSharedPreference()) | |||
{ | |||
if (sharedPrefs == null) | |||
return false; | |||
return sharedPrefs.Contains(key); | |||
} | |||
} | |||
} | |||
#endregion | |||
#region ISettingsServiceImplementation | |||
public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); | |||
public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); | |||
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public void Remove(string key) | |||
{ | |||
if (Application.Context == null) | |||
return; | |||
lock (_locker) | |||
{ | |||
using (var sharedPrefs = GetSharedPreference()) | |||
{ | |||
using (var editor = sharedPrefs.Edit()) | |||
{ | |||
editor.Remove(key); | |||
editor.Commit(); | |||
} | |||
} | |||
} | |||
} | |||
#endregion | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
/* | |||
// Helpers/Settings.cs This file was automatically added when you installed the Settings Plugin. If you are not using a PCL then comment this file back in to use it. | |||
using Plugin.Settings; | |||
using Plugin.Settings.Abstractions; | |||
namespace eShopOnContainers.TestRunner.Droid.Helpers | |||
{ | |||
/// <summary> | |||
/// This is the Settings static class that can be used in your Core solution or in any | |||
/// of your client applications. All settings are laid out the same exact way with getters | |||
/// and setters. | |||
/// </summary> | |||
public static class Settings | |||
{ | |||
private static ISettings AppSettings | |||
{ | |||
get | |||
{ | |||
return CrossSettings.Current; | |||
} | |||
} | |||
#region Setting Constants | |||
private const string SettingsKey = "settings_key"; | |||
private static readonly string SettingsDefault = string.Empty; | |||
#endregion | |||
public static string GeneralSettings | |||
{ | |||
get | |||
{ | |||
return AppSettings.GetValueOrDefault(SettingsKey, SettingsDefault); | |||
} | |||
set | |||
{ | |||
AppSettings.AddOrUpdateValue(SettingsKey, value); | |||
} | |||
} | |||
} | |||
}*/ |
@ -1,44 +0,0 @@ | |||
/* | |||
// Helpers/Settings.cs This file was automatically added when you installed the Settings Plugin. If you are not using a PCL then comment this file back in to use it. | |||
using Plugin.Settings; | |||
using Plugin.Settings.Abstractions; | |||
namespace eShopOnContainers.TestRunner.iOS.Helpers | |||
{ | |||
/// <summary> | |||
/// This is the Settings static class that can be used in your Core solution or in any | |||
/// of your client applications. All settings are laid out the same exact way with getters | |||
/// and setters. | |||
/// </summary> | |||
public static class Settings | |||
{ | |||
private static ISettings AppSettings | |||
{ | |||
get | |||
{ | |||
return CrossSettings.Current; | |||
} | |||
} | |||
#region Setting Constants | |||
private const string SettingsKey = "settings_key"; | |||
private static readonly string SettingsDefault = string.Empty; | |||
#endregion | |||
public static string GeneralSettings | |||
{ | |||
get | |||
{ | |||
return AppSettings.GetValueOrDefault(SettingsKey, SettingsDefault); | |||
} | |||
set | |||
{ | |||
AppSettings.AddOrUpdateValue(SettingsKey, value); | |||
} | |||
} | |||
} | |||
}*/ |
@ -1,50 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.ComponentModel; | |||
namespace eShopOnContainers.UnitTests.Helpers | |||
{ | |||
public class PropertyChangeTracker | |||
{ | |||
List<string> _notifications = new List<string>(); | |||
public PropertyChangeTracker(INotifyPropertyChanged changer) | |||
{ | |||
changer.PropertyChanged += (sender, e) => _notifications.Add(e.PropertyName + ".Value"); | |||
} | |||
//public string[] ChangedProperties | |||
//{ | |||
// get { return _notifications.ToArray(); } | |||
//} | |||
public bool WaitForChange(string propertyName, int maxWaitMilliSeconds) | |||
{ | |||
var startTime = DateTime.UtcNow; | |||
while (!_notifications.Contains(propertyName)) | |||
{ | |||
if (startTime.AddMilliseconds(maxWaitMilliSeconds) < DateTime.UtcNow) | |||
return false; | |||
} | |||
return true; | |||
} | |||
public bool WaitForChange(string propertyName, TimeSpan maxWait) | |||
{ | |||
var startTime = DateTime.UtcNow; | |||
while (!_notifications.Contains(propertyName)) | |||
{ | |||
if (startTime + maxWait < DateTime.UtcNow) | |||
return false; | |||
} | |||
return true; | |||
} | |||
public void Reset() | |||
{ | |||
_notifications.Clear(); | |||
} | |||
} | |||
} |
@ -0,0 +1,65 @@ | |||
using eShopOnContainers.Core.Services.Settings; | |||
using System; | |||
namespace eShopOnContainers.UnitTests.Mocks | |||
{ | |||
public class MockSettingsService : ISettingsService | |||
{ | |||
string _accessTokenDefault = string.Empty; | |||
string _idTokenDefault = string.Empty; | |||
bool _useMocksDefault = true; | |||
string _urlBaseDefault = "https://13.88.8.119"; | |||
bool _useFakeLocationDefault = false; | |||
bool _allowGpsLocationDefault = false; | |||
double _fakeLatitudeDefault = 47.604610d; | |||
double _fakeLongitudeDefault = -122.315752d; | |||
public string AuthAccessToken | |||
{ | |||
get { return _accessTokenDefault; } | |||
set { _accessTokenDefault = value; } | |||
} | |||
public string AuthIdToken | |||
{ | |||
get { return _idTokenDefault; } | |||
set { _idTokenDefault = value; } | |||
} | |||
public bool UseMocks | |||
{ | |||
get { return _useMocksDefault; } | |||
set { _useMocksDefault = value; } | |||
} | |||
public string UrlBase | |||
{ | |||
get { return _urlBaseDefault; } | |||
set { _urlBaseDefault = value; } | |||
} | |||
public bool UseFakeLocation | |||
{ | |||
get { return _useFakeLocationDefault; } | |||
set { _useFakeLocationDefault = value; } | |||
} | |||
public string Latitude | |||
{ | |||
get { return _fakeLatitudeDefault.ToString(); } | |||
set { _fakeLatitudeDefault = Convert.ToDouble(value); } | |||
} | |||
public string Longitude | |||
{ | |||
get { return _fakeLongitudeDefault.ToString(); } | |||
set { _fakeLongitudeDefault = Convert.ToDouble(value); } | |||
} | |||
public bool AllowGpsLocation | |||
{ | |||
get { return _allowGpsLocationDefault; } | |||
set { _allowGpsLocationDefault = value; } | |||
} | |||
} | |||
} |
@ -1,54 +1,57 @@ | |||
using Xunit; | |||
using eShopOnContainers.Core.Models.Navigation; | |||
using eShopOnContainers.Core.Services.Settings; | |||
using eShopOnContainers.Core.ViewModels; | |||
using eShopOnContainers.Core.ViewModels.Base; | |||
using eShopOnContainers.Core.Models.Navigation; | |||
using eShopOnContainers.UnitTests.Mocks; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
namespace eShopOnContainers.UnitTests | |||
{ | |||
public class MainViewModelTests | |||
{ | |||
public MainViewModelTests() | |||
{ | |||
ViewModelLocator.UpdateDependencies(true); | |||
} | |||
public class MainViewModelTests | |||
{ | |||
public MainViewModelTests() | |||
{ | |||
ViewModelLocator.UpdateDependencies(true); | |||
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>(); | |||
} | |||
[Fact] | |||
public void SettingsCommandIsNotNullWhenViewModelInstantiatedTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
Assert.NotNull(mainViewModel.SettingsCommand); | |||
} | |||
[Fact] | |||
public void SettingsCommandIsNotNullWhenViewModelInstantiatedTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
Assert.NotNull(mainViewModel.SettingsCommand); | |||
} | |||
[Fact] | |||
public async Task ViewModelInitializationSendsChangeTabMessageTest() | |||
{ | |||
bool messageReceived = false; | |||
var mainViewModel = new MainViewModel(); | |||
var tabParam = new TabParameter { TabIndex = 2 }; | |||
[Fact] | |||
public async Task ViewModelInitializationSendsChangeTabMessageTest() | |||
{ | |||
bool messageReceived = false; | |||
var mainViewModel = new MainViewModel(); | |||
var tabParam = new TabParameter { TabIndex = 2 }; | |||
Xamarin.Forms.MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) => | |||
{ | |||
messageReceived = true; | |||
}); | |||
await mainViewModel.InitializeAsync(tabParam); | |||
Xamarin.Forms.MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) => | |||
{ | |||
messageReceived = true; | |||
}); | |||
await mainViewModel.InitializeAsync(tabParam); | |||
Assert.True(messageReceived); | |||
} | |||
Assert.True(messageReceived); | |||
} | |||
[Fact] | |||
public void IsBusyPropertyIsFalseWhenViewModelInstantiatedTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
Assert.False(mainViewModel.IsBusy); | |||
} | |||
[Fact] | |||
public void IsBusyPropertyIsFalseWhenViewModelInstantiatedTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
Assert.False(mainViewModel.IsBusy); | |||
} | |||
[Fact] | |||
public async Task IsBusyPropertyIsTrueAfterViewModelInitializationTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
await mainViewModel.InitializeAsync(null); | |||
Assert.True(mainViewModel.IsBusy); | |||
} | |||
} | |||
[Fact] | |||
public async Task IsBusyPropertyIsTrueAfterViewModelInitializationTest() | |||
{ | |||
var mainViewModel = new MainViewModel(); | |||
await mainViewModel.InitializeAsync(null); | |||
Assert.True(mainViewModel.IsBusy); | |||
} | |||
} | |||
} |
@ -1,55 +1,61 @@ | |||
using Xunit; | |||
using eShopOnContainers.Core; | |||
using eShopOnContainers.Core; | |||
using eShopOnContainers.Core.Services.Order; | |||
using eShopOnContainers.Core.Services.Settings; | |||
using eShopOnContainers.Core.ViewModels; | |||
using eShopOnContainers.Core.ViewModels.Base; | |||
using eShopOnContainers.Core.Services.Order; | |||
using eShopOnContainers.UnitTests.Mocks; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
namespace eShopOnContainers.UnitTests | |||
{ | |||
public class OrderViewModelTests | |||
{ | |||
public OrderViewModelTests() | |||
{ | |||
ViewModelLocator.UpdateDependencies(true); | |||
} | |||
[Fact] | |||
public void OrderPropertyIsNullWhenViewModelInstantiatedTest() | |||
{ | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(orderService); | |||
Assert.Null(orderViewModel.Order); | |||
} | |||
[Fact] | |||
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest() | |||
{ | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(orderService); | |||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken); | |||
await orderViewModel.InitializeAsync(order); | |||
Assert.NotNull(orderViewModel.Order); | |||
} | |||
[Fact] | |||
public async Task SettingOrderPropertyShouldRaisePropertyChanged() | |||
{ | |||
bool invoked = false; | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(orderService); | |||
orderViewModel.PropertyChanged += (sender, e) => | |||
{ | |||
if (e.PropertyName.Equals("Order")) | |||
invoked = true; | |||
}; | |||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken); | |||
await orderViewModel.InitializeAsync(order); | |||
Assert.True(invoked); | |||
} | |||
} | |||
public class OrderViewModelTests | |||
{ | |||
public OrderViewModelTests() | |||
{ | |||
ViewModelLocator.UpdateDependencies(true); | |||
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>(); | |||
} | |||
[Fact] | |||
public void OrderPropertyIsNullWhenViewModelInstantiatedTest() | |||
{ | |||
var settingsService = new MockSettingsService(); | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(settingsService, orderService); | |||
Assert.Null(orderViewModel.Order); | |||
} | |||
[Fact] | |||
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest() | |||
{ | |||
var settingsService = new MockSettingsService(); | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(settingsService, orderService); | |||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken); | |||
await orderViewModel.InitializeAsync(order); | |||
Assert.NotNull(orderViewModel.Order); | |||
} | |||
[Fact] | |||
public async Task SettingOrderPropertyShouldRaisePropertyChanged() | |||
{ | |||
bool invoked = false; | |||
var settingsService = new MockSettingsService(); | |||
var orderService = new OrderMockService(); | |||
var orderViewModel = new OrderDetailViewModel(settingsService, orderService); | |||
orderViewModel.PropertyChanged += (sender, e) => | |||
{ | |||
if (e.PropertyName.Equals("Order")) | |||
invoked = true; | |||
}; | |||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken); | |||
await orderViewModel.InitializeAsync(order); | |||
Assert.True(invoked); | |||
} | |||
} | |||
} |
@ -0,0 +1,99 @@ | |||
using eShopOnContainers.Core.Services.Settings; | |||
using eShopOnContainers.Windows.Services; | |||
using Windows.Storage; | |||
[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] | |||
namespace eShopOnContainers.Windows.Services | |||
{ | |||
public class SettingsServiceImplementation : ISettingsServiceImplementation | |||
{ | |||
#region Internal Implementation | |||
readonly object _locker = new object(); | |||
ApplicationDataContainer GetAppSettings() | |||
{ | |||
return ApplicationData.Current.LocalSettings; | |||
} | |||
bool AddOrUpdateValueInternal<T>(string key, T value) | |||
{ | |||
bool valueChanged = false; | |||
if (value == null) | |||
{ | |||
Remove(key); | |||
return true; | |||
} | |||
lock (_locker) | |||
{ | |||
var settings = GetAppSettings(); | |||
if (settings.Values.ContainsKey(key)) | |||
{ | |||
if (settings.Values[key] != (object)value) | |||
{ | |||
settings.Values[key] = value; | |||
valueChanged = true; | |||
} | |||
} | |||
else | |||
{ | |||
settings.Values[key] = value; | |||
valueChanged = true; | |||
} | |||
} | |||
return valueChanged; | |||
} | |||
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T)) | |||
{ | |||
object value; | |||
lock (_locker) | |||
{ | |||
var settings = GetAppSettings(); | |||
if (settings.Values.ContainsKey(key)) | |||
{ | |||
var tempValue = settings.Values[key]; | |||
if (tempValue != null) | |||
value = (T)tempValue; | |||
else | |||
value = defaultValue; | |||
} | |||
else | |||
{ | |||
value = defaultValue; | |||
} | |||
} | |||
return null != value ? (T)value : defaultValue; | |||
} | |||
#endregion | |||
#region ISettingsServiceImplementation | |||
public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); | |||
public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); | |||
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public void Remove(string key) | |||
{ | |||
lock (_locker) | |||
{ | |||
var settings = GetAppSettings(); | |||
if (settings.Values.ContainsKey(key)) | |||
{ | |||
settings.Values.Remove(key); | |||
} | |||
} | |||
} | |||
#endregion | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
/* | |||
// Helpers/Settings.cs This file was automatically added when you installed the Settings Plugin. If you are not using a PCL then comment this file back in to use it. | |||
using Plugin.Settings; | |||
using Plugin.Settings.Abstractions; | |||
namespace eShopOnContainers.iOS.Helpers | |||
{ | |||
/// <summary> | |||
/// This is the Settings static class that can be used in your Core solution or in any | |||
/// of your client applications. All settings are laid out the same exact way with getters | |||
/// and setters. | |||
/// </summary> | |||
public static class Settings | |||
{ | |||
private static ISettings AppSettings | |||
{ | |||
get | |||
{ | |||
return CrossSettings.Current; | |||
} | |||
} | |||
#region Setting Constants | |||
private const string SettingsKey = "settings_key"; | |||
private static readonly string SettingsDefault = string.Empty; | |||
#endregion | |||
public static string GeneralSettings | |||
{ | |||
get | |||
{ | |||
return AppSettings.GetValueOrDefault(SettingsKey, SettingsDefault); | |||
} | |||
set | |||
{ | |||
AppSettings.AddOrUpdateValue(SettingsKey, value); | |||
} | |||
} | |||
} | |||
}*/ |
@ -0,0 +1,128 @@ | |||
using eShopOnContainers.Core.Services.Settings; | |||
using eShopOnContainers.iOS.Services; | |||
using Foundation; | |||
using System; | |||
[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] | |||
namespace eShopOnContainers.iOS.Services | |||
{ | |||
public class SettingsServiceImplementation : ISettingsServiceImplementation | |||
{ | |||
#region Internal Implementation | |||
readonly object _locker = new object(); | |||
NSUserDefaults GetUserDefaults() => NSUserDefaults.StandardUserDefaults; | |||
bool AddOrUpdateValueInternal<T>(string key, T value) | |||
{ | |||
if (value == null) | |||
{ | |||
Remove(key); | |||
return true; | |||
} | |||
var type = typeof(T); | |||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) | |||
{ | |||
type = Nullable.GetUnderlyingType(type); | |||
} | |||
var typeCode = Type.GetTypeCode(type); | |||
lock (_locker) | |||
{ | |||
var defaults = GetUserDefaults(); | |||
switch (typeCode) | |||
{ | |||
case TypeCode.Boolean: | |||
defaults.SetBool(Convert.ToBoolean(value), key); | |||
break; | |||
case TypeCode.String: | |||
defaults.SetString(Convert.ToString(value), key); | |||
break; | |||
default: | |||
throw new ArgumentException($"Value of type {typeCode} is unsupported."); | |||
} | |||
try | |||
{ | |||
defaults.Synchronize(); | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message); | |||
} | |||
} | |||
return true; | |||
} | |||
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T)) | |||
{ | |||
lock (_locker) | |||
{ | |||
var defaults = GetUserDefaults(); | |||
if (defaults[key] == null) | |||
{ | |||
return defaultValue; | |||
} | |||
var type = typeof(T); | |||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) | |||
{ | |||
type = Nullable.GetUnderlyingType(type); | |||
} | |||
object value = null; | |||
var typeCode = Type.GetTypeCode(type); | |||
switch (typeCode) | |||
{ | |||
case TypeCode.Boolean: | |||
value = defaults.BoolForKey(key); | |||
break; | |||
case TypeCode.String: | |||
value = defaults.StringForKey(key); | |||
break; | |||
default: | |||
throw new ArgumentException($"Value of type {typeCode} is unsupported."); | |||
} | |||
return null != value ? (T)value : defaultValue; | |||
} | |||
} | |||
#endregion | |||
#region ISettingsServiceImplementation | |||
public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); | |||
public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); | |||
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); | |||
public void Remove(string key) | |||
{ | |||
lock (_locker) | |||
{ | |||
var defaults = GetUserDefaults(); | |||
try | |||
{ | |||
if (defaults[key] != null) | |||
{ | |||
defaults.RemoveObject(key); | |||
defaults.Synchronize(); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message); | |||
} | |||
} | |||
} | |||
#endregion | |||
} | |||
} |