///* //See LICENSE folder for this sample’s licensing information. //Abstract: //A data manager that manages data conforming to `Codable` and stores it in `UserDefaults`. //*/ //using System; //using Foundation; //using CoreFoundation; //namespace SiriKit //{ // public struct UserDefaultsStorageDescriptor // { // public string Key { get; set; } // public UserDefaultsStorageDescriptor(string key) // { // Key = key; // } // } // public static class NotificationKeys // { // // Clients of `DataManager` that want to know when the data changes can // // listen for this notification. // public const string DataChanged = "DataChangedNotification"; // } // public class DataManager : NSObject // where ManagedDataType : NSObject, INSCoding // { // // This sample uses App Groups to share a suite of data between the // // main app and the different extensions. // protected NSUserDefaults UserDefaults = NSUserDefaultsHelper.DataSuite; // // To prevent data races, all access to `UserDefaults` uses this queue. // protected DispatchQueue UserDefaultsAccessQueue = new DispatchQueue("User Defaults Access Queue"); // // Storage and observation information. // protected UserDefaultsStorageDescriptor StorageDescriptor; // // A flag to avoid receiving notifications about data this instance just // // wrote to `UserDefaults`. // protected bool IgnoreLocalUserDefaultsChanges = false; // // The observer object handed back after registering to observe a // // property. // IDisposable UserDefaultsObserver; // // The data managed by this `DataManager`. // //此“DataManager”管理的数据。 // protected ManagedDataType ManagedDataBackingInstance; // // Access to `managedDataBackingInstance` needs to occur on a dedicated // // queue to avoid data races. // protected DispatchQueue DataAccessQueue = new DispatchQueue("Data Access Queue"); // // Public access to the managed data for clients of `DataManager` // public ManagedDataType ManagedData // { // get // { // ManagedDataType data = null; // DataAccessQueue.DispatchSync(() => data = ManagedDataBackingInstance); // return data; // } // } // // See note below about createInitialData and initialData // public DataManager(UserDefaultsStorageDescriptor storageDescriptor, ManagedDataType initialData) // { // StorageDescriptor = storageDescriptor; // LoadData(); // if (ManagedDataBackingInstance is null) // { // ManagedDataBackingInstance = initialData; // WriteData(); // } // ObserveChangesInUserDefaults(); // } // // createInitialData // // // // The Swift version of this app has a createInitialData method. // // Each child class of the DataManager class overrides this method, and // // then the DataManager base class calls the derived versions to get // // the initial data. C# gives a compiler warning for this ("Virtual // // member call in constructor"). Since in C# the base class constructor // // is run before the child class constructor, having the base clas // // constructor call out to a method on the derived class is calling // // a method on an object that has not yet been fully constructed. // // The C# version of this sample works around this problem by passing // // in the initial data to the constructor. // void ObserveChangesInUserDefaults() // { // var weakThis = new WeakReference>(this); // Action changeHandler = (change) => // { // if (weakThis.TryGetTarget(out var dataManager)) // { // // Ignore any change notifications coming from data this // // instance just saved to `NSUserDefaults`. // if (dataManager is null || dataManager.IgnoreLocalUserDefaultsChanges) // { // return; // } // // The underlying data changed in `NSUserDefaults`, so // // update this instance with the change and notify clients // // of the change. // dataManager.LoadData(); // dataManager.NotifyClientsDataChanged(); // } // }; // UserDefaultsObserver = UserDefaults.AddObserver( // StorageDescriptor.Key, // NSKeyValueObservingOptions.Initial | NSKeyValueObservingOptions.New, // changeHandler // ); // } // // Notifies clients the data changed by posting an `NSNotification` with // // the key `NotificationKeys.DataChanged` // void NotifyClientsDataChanged() // { // var notification = NSNotification.FromName(NotificationKeys.DataChanged, this); // NSNotificationCenter.DefaultCenter.PostNotification(notification); // } // protected virtual void FinishUnarchiving(NSObject unarchivedData) // { // throw new NotImplementedException(); // } // // Loads the data from `NSUserDefaults`. // void LoadData() // { // UserDefaultsAccessQueue.DispatchSync(() => // { // NSData archivedData = UserDefaults.DataForKey(StorageDescriptor.Key); // try // { // // Let the derived classes handle the specifics of // // putting the unarchived data in the correct format. // // This is necessary because the derived classes // // (SoupMenuManager, SoupOrderMenuManager) are using // // generic data formats (NSMutableSet or NSMutableArray) // // and these types cannot be casted directly from the // // deserialized data. // NSObject unarchivedData = NSKeyedUnarchiver.UnarchiveObject(archivedData); // FinishUnarchiving(unarchivedData); // } // catch (Exception e) // { // if (!(e is null)) // { // Console.WriteLine($"Error: {e.Message}"); // } // } // }); // } // // Writes the data to `NSUserDefaults` // protected void WriteData() // { // UserDefaultsAccessQueue.DispatchAsync(() => // { // try // { // NSData encodedData = NSKeyedArchiver.ArchivedDataWithRootObject(ManagedDataBackingInstance); // IgnoreLocalUserDefaultsChanges = true; // UserDefaults.SetValueForKey(encodedData, (NSString)StorageDescriptor.Key); // IgnoreLocalUserDefaultsChanges = false; // NotifyClientsDataChanged(); // } // catch (Exception e) // { // throw new Exception($"Could not save data. Reason: {e.Message}"); // } // }); // } // } //}