|
1 | 1 | import Foundation |
2 | 2 |
|
| 3 | +/// Automatically syncs `UserDefaults` values to iCloud across all of a user's devices. |
| 4 | +/// Values are synced when their key matches a specified prefix. |
| 5 | +/// |
| 6 | +/// Create an instance somewhere it will stay in scope, then call ``start(prefix:)``. |
| 7 | +/// |
| 8 | +/// ```swift |
| 9 | +/// // AppDelegate |
| 10 | +/// let cloudUserDefaults = CloudUserDefaults() |
| 11 | +/// cloudUserDefaults.start(prefix: "cloud_") |
| 12 | +/// ``` |
3 | 13 | open class CloudUserDefaults { |
4 | 14 |
|
| 15 | + /// Posted when values have been received from iCloud. |
| 16 | + /// The notification object contains the full iCloud key-value dictionary. |
5 | 17 | public static let cloudSyncNotification = Notification.Name("CloudSyncNotification") |
| 18 | + |
6 | 19 | internal var prefix: String! |
| 20 | + private let defaults: UserDefaults |
7 | 21 |
|
8 | | - public init() {} |
| 22 | + /// Creates a new instance using the specified `UserDefaults` store. |
| 23 | + /// - Parameter defaults: The `UserDefaults` instance to sync. Defaults to `UserDefaults.standard`. |
| 24 | + /// Pass a suite-based instance for app groups and widgets. |
| 25 | + public init(defaults: UserDefaults = .standard) { |
| 26 | + self.defaults = defaults |
| 27 | + } |
9 | 28 |
|
| 29 | + /// Begins listening for changes to `UserDefaults` and iCloud, syncing any key that starts with the given prefix. |
| 30 | + /// - Parameter prefix: The key prefix that identifies values to sync, e.g. `"cloud_"`. |
10 | 31 | public func start(prefix: String) { |
11 | 32 | self.prefix = prefix |
12 | 33 | NotificationCenter.default.addObserver(self, selector: #selector(notificationFromCloud(notification:)), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: nil) |
13 | | - NotificationCenter.default.addObserver(self, selector: #selector(notifyCloud(notification:)), name: UserDefaults.didChangeNotification, object: nil) |
| 34 | + NotificationCenter.default.addObserver(self, selector: #selector(notifyCloud(notification:)), name: UserDefaults.didChangeNotification, object: defaults) |
14 | 35 | } |
15 | 36 |
|
16 | 37 | @objc internal func notificationFromCloud(notification: NSNotification) { |
17 | 38 | let dict = NSUbiquitousKeyValueStore.default.dictionaryRepresentation |
18 | | - |
19 | 39 | // Disable notifications to cloud while we set local values from cloud |
20 | | - NotificationCenter.default.removeObserver(self, name: UserDefaults.didChangeNotification, object: nil) |
21 | | - |
| 40 | + NotificationCenter.default.removeObserver(self, name: UserDefaults.didChangeNotification, object: defaults) |
22 | 41 | for (key, value) in dict { |
23 | 42 | if key.hasPrefix(prefix) { |
24 | | - UserDefaults.standard.set(value, forKey: key) |
| 43 | + defaults.set(value, forKey: key) |
25 | 44 | } |
26 | 45 | } |
27 | | - |
28 | 46 | // Resume notifications to cloud |
29 | | - NotificationCenter.default.addObserver(self, selector: #selector(notifyCloud(notification:)), name: UserDefaults.didChangeNotification, object: nil) |
30 | | - |
| 47 | + NotificationCenter.default.addObserver(self, selector: #selector(notifyCloud(notification:)), name: UserDefaults.didChangeNotification, object: defaults) |
31 | 48 | // Send a message with cloud payload that app can listen to |
32 | 49 | NotificationCenter.default.post(name: CloudUserDefaults.cloudSyncNotification, object: dict) |
33 | 50 | } |
34 | 51 |
|
35 | 52 | @objc internal func notifyCloud(notification: NSNotification) { |
36 | | - let dict = UserDefaults.standard.dictionaryRepresentation() |
| 53 | + let dict = defaults.dictionaryRepresentation() |
37 | 54 | for (key, value) in dict { |
38 | 55 | if key.hasPrefix(prefix) { |
39 | 56 | NSUbiquitousKeyValueStore.default.set(value, forKey: key) |
|
0 commit comments