Skip to content

Commit

Permalink
Disable syncing from local if its default after iCloud.add
Browse files Browse the repository at this point in the history
  • Loading branch information
hank121314 committed Aug 5, 2024
1 parent bf71746 commit caeb02b
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 21 deletions.
32 changes: 17 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,8 @@ 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)
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 +262,20 @@ 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
16 changes: 16 additions & 0 deletions Sources/Defaults/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,22 @@ extension Defaults.Key {
) where Value == T? {
self.init(name, default: nil, suite: suite, iCloud: iCloud)
}

/**
Check whether the stored value is the default value.
*/
public func 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 {
Expand Down
10 changes: 10 additions & 0 deletions Sources/Defaults/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ extension Collection {
}
}

extension Equatable {
func isEqual(_ rhs: any Equatable) -> Bool {
if let rhs = rhs as? Self, rhs == self {
return true
}

return false
}
}

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 caeb02b

Please sign in to comment.