Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable syncing from local if its default after iCloud.add #185

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@

## 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 @@
/**
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)
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untested and would require Swift 6, but maybe:

public static func add<each Value: Defaults.Serializable>(_ keys: repeat [Defaults.Key<each Value>]) {
	for key in repeat each keys {
		add(key)
	}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, will add a TODO for it first!


/**
Expand Down Expand Up @@ -269,11 +264,23 @@
/**
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 Expand Up @@ -539,7 +546,7 @@
if
let localTimestamp = self.timestamp(forKey: key, source: .local),
localTimestamp >= remoteTimestamp
{

Check warning on line 549 in Sources/Defaults/Defaults+iCloud.swift

View workflow job for this annotation

GitHub Actions / lint

Opening Brace Spacing Violation: Opening braces should be preceded by a single space and on the same line as the declaration (opening_brace)
continue
}

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 Equatable {
func isEqual(_ rhs: any Equatable) -> Bool {
sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
guard
let rhs = rhs as? Self,
rhs == self
else {
return false
}

return true
}
}

extension Defaults {
@usableFromInline
static func isValidKeyPath(name: String) -> Bool {
Expand All @@ -191,7 +204,7 @@
if
T.isNativelySupportedType,
let anyObject = anyObject as? T
{

Check warning on line 207 in Sources/Defaults/Utilities.swift

View workflow job for this annotation

GitHub Actions / lint

Opening Brace Spacing Violation: Opening braces should be preceded by a single space and on the same line as the declaration (opening_brace)
return anyObject
}

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