Skip to content

Commit

Permalink
Disable syncing from local if its default after iCloud.add (#185)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
hank121314 and sindresorhus authored Aug 12, 2024
1 parent bf71746 commit 174b791
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 21 deletions.
37 changes: 22 additions & 15 deletions Sources/Defaults/Defaults+iCloud.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension Defaults {
## 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
import Defaults
Expand Down Expand Up @@ -82,15 +82,10 @@ extension Defaults {
/**
Add the keys to be automatically synced.
*/
public static func add(_ keys: Defaults.Keys...) {
synchronizer.add(keys)
}

/**
Add the keys to be automatically synced.
*/
public static func add(_ keys: [Defaults.Keys]) {
synchronizer.add(keys)
// TODO: Support array of Defaults.Key after Swift 6 pack iteration is supported.
// 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)
}

/**
Expand Down Expand Up @@ -269,11 +264,23 @@ final class iCloudSynchronizer {
/**
Add new key and start to observe its changes.
*/
func add(_ keys: [Defaults.Keys]) {
self.keys.formUnion(keys)
syncWithoutWaiting(keys)
for key in keys {
localKeysMonitor.add(key: key)
func add<Value: Defaults.Serializable>(_ key: Defaults.Key<Value>) {
let (isInserted, _) = self.keys.insert(key)
guard isInserted else {
return
}

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])
}
}

Expand Down
25 changes: 25 additions & 0 deletions Sources/Defaults/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,31 @@ extension Defaults.Key {
) where Value == T? {
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 {
Expand Down
13 changes: 13 additions & 0 deletions Sources/Defaults/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
@usableFromInline
static func isValidKeyPath(name: String) -> Bool {
Expand Down
11 changes: 5 additions & 6 deletions Tests/DefaultsTests/Defaults+iCloudTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,12 @@ final class DefaultsICloudTests: XCTestCase {
}

func testICloudInitialize() async {
print(Defaults.iCloud.keys)
let name = Defaults.Key<String>("testICloudInitialize_name", default: "0", iCloud: true)
let quality = Defaults.Key<Double>("testICloudInitialize_quality", default: 0.0, iCloud: true)

print(Defaults.iCloud.keys)
await Defaults.iCloud.waitForSyncCompletion()
XCTAssertEqual(mockStorage.data(forKey: name.name), "0")
XCTAssertEqual(mockStorage.data(forKey: quality.name), 0.0)
XCTAssertNil(mockStorage.data(forKey: name.name))
XCTAssertNil(mockStorage.data(forKey: quality.name))
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]

Expand Down Expand Up @@ -251,8 +249,9 @@ final class DefaultsICloudTests: XCTestCase {

func testAddFromDetached() async {
let name = Defaults.Key<String>("testInitAddFromDetached_name", default: "0")
let quantity = Defaults.Key<Bool>("testInitAddFromDetached_quantity", default: false)
let task = Task.detached {
Defaults.iCloud.add(name)
Defaults.iCloud.add(name, quantity)
Defaults.iCloud.syncWithoutWaiting()
await Defaults.iCloud.waitForSyncCompletion()
}
Expand All @@ -268,7 +267,7 @@ final class DefaultsICloudTests: XCTestCase {
let name = Defaults.Key<String>("testICloudInitializeFromDetached_name", default: "0", iCloud: true)

await Defaults.iCloud.waitForSyncCompletion()
XCTAssertEqual(mockStorage.data(forKey: name.name), "0")
XCTAssertNil(mockStorage.data(forKey: name.name))
}
await task.value
}
Expand Down
7 changes: 7 additions & 0 deletions Tests/DefaultsTests/DefaultsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ final class DefaultsTests: XCTestCase {
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() {
let key = Defaults.Key<Bool>("observeKey", default: false)
let expect = expectation(description: "Observation closure being called")
Expand Down

0 comments on commit 174b791

Please sign in to comment.