wxr
2021-11-30 e75ccccb370b47305c6eadb321efb27c86cdd28b
SiriKit/Data/DataManager.cs
New file
@@ -0,0 +1,188 @@
///*
//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<ManagedDataType> : 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<DataManager<ManagedDataType>>(this);
//            Action<NSObservedChange> 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<T> or NSMutableArray<T>)
//                    // 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}");
//                }
//            });
//        }
//    }
//}