Disable syncing from local if its default after `iCloud.add` (#185)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
parent
bf717462d9
commit
174b791fec
|
@ -39,7 +39,7 @@ extension Defaults {
|
||||||
|
|
||||||
## Dynamically Toggle Syncing
|
## Dynamically Toggle Syncing
|
||||||
|
|
||||||
You can also toggle the syncing behavior dynamically using the ``Defaults/iCloud/add(_:)-5gffb`` and ``Defaults/iCloud/remove(_:)-1b8w5`` methods.
|
You can also toggle the syncing behavior dynamically using the ``Defaults/iCloud/add(_:)`` and ``Defaults/iCloud/remove(_:)-1b8w5`` methods.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import Defaults
|
import Defaults
|
||||||
|
@ -82,15 +82,10 @@ extension Defaults {
|
||||||
/**
|
/**
|
||||||
Add the keys to be automatically synced.
|
Add the keys to be automatically synced.
|
||||||
*/
|
*/
|
||||||
public static func add(_ keys: Defaults.Keys...) {
|
// TODO: Support array of Defaults.Key after Swift 6 pack iteration is supported.
|
||||||
synchronizer.add(keys)
|
// https://github.com/sindresorhus/Defaults/pull/185#discussion_r1704464183
|
||||||
}
|
public static func add<each Value: Defaults.Serializable>(_ keys: repeat Defaults.Key<each Value>) {
|
||||||
|
repeat synchronizer.add(each keys)
|
||||||
/**
|
|
||||||
Add the keys to be automatically synced.
|
|
||||||
*/
|
|
||||||
public static func add(_ keys: [Defaults.Keys]) {
|
|
||||||
synchronizer.add(keys)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,11 +264,23 @@ final class iCloudSynchronizer {
|
||||||
/**
|
/**
|
||||||
Add new key and start to observe its changes.
|
Add new key and start to observe its changes.
|
||||||
*/
|
*/
|
||||||
func add(_ keys: [Defaults.Keys]) {
|
func add<Value: Defaults.Serializable>(_ key: Defaults.Key<Value>) {
|
||||||
self.keys.formUnion(keys)
|
let (isInserted, _) = self.keys.insert(key)
|
||||||
syncWithoutWaiting(keys)
|
guard isInserted else {
|
||||||
for key in keys {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
localKeysMonitor.add(key: key)
|
localKeysMonitor.add(key: key)
|
||||||
|
|
||||||
|
// If the local value is the default value, only sync from remote, since all devices should already have the default value.
|
||||||
|
if key._isDefaultValue {
|
||||||
|
guard case .remote = latestDataSource(forKey: key) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
syncWithoutWaiting([key], .remote)
|
||||||
|
} else {
|
||||||
|
syncWithoutWaiting([key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,31 @@ extension Defaults.Key {
|
||||||
) where Value == T? {
|
) where Value == T? {
|
||||||
self.init(name, default: nil, suite: suite, iCloud: iCloud)
|
self.init(name, default: nil, suite: suite, iCloud: iCloud)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check whether the stored value is the default value.
|
||||||
|
|
||||||
|
- Note: This is only for internal use because it would not work for non-equatable values.
|
||||||
|
*/
|
||||||
|
var _isDefaultValue: Bool {
|
||||||
|
let defaultValue = defaultValue
|
||||||
|
let value = suite[self]
|
||||||
|
guard
|
||||||
|
let defaultValue = defaultValue as? any Equatable,
|
||||||
|
let value = value as? any Equatable
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue.isEqual(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Defaults.Key where Value: Equatable {
|
||||||
|
/**
|
||||||
|
Check whether the stored value is the default value.
|
||||||
|
*/
|
||||||
|
public var isDefaultValue: Bool { self._isDefaultValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
|
|
|
@ -165,6 +165,19 @@ extension Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Equatable {
|
||||||
|
func isEqual(_ rhs: any Equatable) -> Bool {
|
||||||
|
guard
|
||||||
|
let rhs = rhs as? Self,
|
||||||
|
rhs == self
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
static func isValidKeyPath(name: String) -> Bool {
|
static func isValidKeyPath(name: String) -> Bool {
|
||||||
|
|
|
@ -88,14 +88,12 @@ final class DefaultsICloudTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testICloudInitialize() async {
|
func testICloudInitialize() async {
|
||||||
print(Defaults.iCloud.keys)
|
|
||||||
let name = Defaults.Key<String>("testICloudInitialize_name", default: "0", iCloud: true)
|
let name = Defaults.Key<String>("testICloudInitialize_name", default: "0", iCloud: true)
|
||||||
let quality = Defaults.Key<Double>("testICloudInitialize_quality", default: 0.0, iCloud: true)
|
let quality = Defaults.Key<Double>("testICloudInitialize_quality", default: 0.0, iCloud: true)
|
||||||
|
|
||||||
print(Defaults.iCloud.keys)
|
|
||||||
await Defaults.iCloud.waitForSyncCompletion()
|
await Defaults.iCloud.waitForSyncCompletion()
|
||||||
XCTAssertEqual(mockStorage.data(forKey: name.name), "0")
|
XCTAssertNil(mockStorage.data(forKey: name.name))
|
||||||
XCTAssertEqual(mockStorage.data(forKey: quality.name), 0.0)
|
XCTAssertNil(mockStorage.data(forKey: quality.name))
|
||||||
let name_expected = ["1", "2", "3", "4", "5", "6", "7"]
|
let name_expected = ["1", "2", "3", "4", "5", "6", "7"]
|
||||||
let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
|
let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
|
||||||
|
|
||||||
|
@ -251,8 +249,9 @@ final class DefaultsICloudTests: XCTestCase {
|
||||||
|
|
||||||
func testAddFromDetached() async {
|
func testAddFromDetached() async {
|
||||||
let name = Defaults.Key<String>("testInitAddFromDetached_name", default: "0")
|
let name = Defaults.Key<String>("testInitAddFromDetached_name", default: "0")
|
||||||
|
let quantity = Defaults.Key<Bool>("testInitAddFromDetached_quantity", default: false)
|
||||||
let task = Task.detached {
|
let task = Task.detached {
|
||||||
Defaults.iCloud.add(name)
|
Defaults.iCloud.add(name, quantity)
|
||||||
Defaults.iCloud.syncWithoutWaiting()
|
Defaults.iCloud.syncWithoutWaiting()
|
||||||
await Defaults.iCloud.waitForSyncCompletion()
|
await Defaults.iCloud.waitForSyncCompletion()
|
||||||
}
|
}
|
||||||
|
@ -268,7 +267,7 @@ final class DefaultsICloudTests: XCTestCase {
|
||||||
let name = Defaults.Key<String>("testICloudInitializeFromDetached_name", default: "0", iCloud: true)
|
let name = Defaults.Key<String>("testICloudInitializeFromDetached_name", default: "0", iCloud: true)
|
||||||
|
|
||||||
await Defaults.iCloud.waitForSyncCompletion()
|
await Defaults.iCloud.waitForSyncCompletion()
|
||||||
XCTAssertEqual(mockStorage.data(forKey: name.name), "0")
|
XCTAssertNil(mockStorage.data(forKey: name.name))
|
||||||
}
|
}
|
||||||
await task.value
|
await task.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,13 @@ final class DefaultsTests: XCTestCase {
|
||||||
Defaults.removeAll(suite: customSuite)
|
Defaults.removeAll(suite: customSuite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testIsDefaultValue() {
|
||||||
|
let key = Defaults.Key<Bool>("isDefaultValue", default: false)
|
||||||
|
XCTAssert(key.isDefaultValue)
|
||||||
|
Defaults[key].toggle()
|
||||||
|
XCTAssert(!key.isDefaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
func testObserveKeyCombine() {
|
func testObserveKeyCombine() {
|
||||||
let key = Defaults.Key<Bool>("observeKey", default: false)
|
let key = Defaults.Key<Bool>("observeKey", default: false)
|
||||||
let expect = expectation(description: "Observation closure being called")
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
Loading…
Reference in New Issue