diff --git a/Sources/AppState/Application/Application+public.swift b/Sources/AppState/Application/Application+public.swift index 5a6e929..19d27d1 100644 --- a/Sources/AppState/Application/Application+public.swift +++ b/Sources/AppState/Application/Application+public.swift @@ -341,10 +341,10 @@ public extension Application { guard let value = cache.get(key, as: Value.self) else { let value = initial() - return State(initial: value, scope: scope) + return State(type: .state, initial: value, scope: scope) } - return State(initial: value, scope: scope) + return State(type: .state, initial: value, scope: scope) } /// Overloaded version of `state(initial:feature:id:)` function where id is generated from the code context. diff --git a/Sources/AppState/Application/Types/Application+State.swift b/Sources/AppState/Application/Types/Application+State.swift index 2eb0938..5ab6fd4 100644 --- a/Sources/AppState/Application/Types/Application+State.swift +++ b/Sources/AppState/Application/Types/Application+State.swift @@ -1,6 +1,14 @@ extension Application { /// `State` encapsulates the value within the application's scope and allows any changes to be propagated throughout the scoped area. public struct State: CustomStringConvertible { + enum StateType { + case state + case stored + case sync + } + + private let type: StateType + /// A private backing storage for the value. private var _value: Value @@ -20,6 +28,7 @@ extension Application { _value = newValue shared.cache.set( value: Application.State( + type: .state, initial: newValue, scope: scope ), @@ -39,15 +48,21 @@ extension Application { - scope: The scope in which the state exists */ init( + type: StateType, initial value: Value, scope: Scope ) { + self.type = type self._value = value self.scope = scope } public var description: String { - "State<\(Value.self)>(\(value)) (\(scope.key))" + switch type { + case .state: "State<\(Value.self)>(\(value)) (\(scope.key))" + case .stored: "StoredState<\(Value.self)>(\(value)) (\(scope.key))" + case .sync: "SyncState<\(Value.self)>(\(value)) (\(scope.key))" + } } } } diff --git a/Sources/AppState/Application/Types/Application+StoredState.swift b/Sources/AppState/Application/Types/Application+StoredState.swift index a0d4caa..eff9a16 100644 --- a/Sources/AppState/Application/Types/Application+StoredState.swift +++ b/Sources/AppState/Application/Types/Application+StoredState.swift @@ -7,7 +7,7 @@ extension Application { } /// `StoredState` encapsulates the value within the application's scope and allows any changes to be propagated throughout the scoped area. State is stored using `UserDefaults`. - public struct StoredState: CustomStringConvertible { + public struct StoredState { @AppDependency(\.userDefaults) private var userDefaults: UserDefaults /// The initial value of the state. @@ -42,6 +42,7 @@ extension Application { } else { shared.cache.set( value: Application.State( + type: .stored, initial: newValue, scope: scope ), @@ -70,10 +71,6 @@ extension Application { self.scope = scope } - public var description: String { - "StoredState<\(Value.self)>(\(value)) (\(scope.key))" - } - /// Removes the value from `UserDefaults` and resets the value to the inital value. public mutating func remove() { value = initial() diff --git a/Sources/AppState/Application/Types/Application+SyncState.swift b/Sources/AppState/Application/Types/Application+SyncState.swift index 36c6720..8c9a8d9 100644 --- a/Sources/AppState/Application/Types/Application+SyncState.swift +++ b/Sources/AppState/Application/Types/Application+SyncState.swift @@ -12,7 +12,7 @@ extension Application { - Note: The key-value store is intended for storing data that changes infrequently. As you test your devices, if the app on a device makes frequent changes to the key-value store, the system may defer the synchronization of some changes in order to minimize the number of round trips to the server. The more frequently the app make changes, the more likely the changes will be deferred and will not immediately show up on the other devices. */ - public struct SyncState: CustomStringConvertible { + public struct SyncState { @AppDependency(\.icloudStore) private var icloudStore: NSUbiquitousKeyValueStore /// The initial value of the state. @@ -24,21 +24,21 @@ extension Application { /// When setting a new value, the value is saved to the iCloud Key-Value Store and the local cache. public var value: Value { get { - let cachedValue = shared.cache.get( - scope.key, - as: State.self - ) - - if let cachedValue = cachedValue { - return cachedValue.value - } + if + let data = icloudStore.data(forKey: scope.key), + let storedValue = try? JSONDecoder().decode(Value.self, from: data) + { + return storedValue + } guard - let data = icloudStore.data(forKey: scope.key), - let value = try? JSONDecoder().decode(Value.self, from: data) + let cachedValue = shared.cache.get( + scope.key, + as: State.self + ) else { return initial() } - return value + return cachedValue.value } set { let mirror = Mirror(reflecting: newValue) @@ -51,6 +51,7 @@ extension Application { } else { shared.cache.set( value: Application.State( + type: .sync, initial: newValue, scope: scope ), @@ -93,10 +94,6 @@ extension Application { self.scope = scope } - public var description: String { - "SyncState<\(Value.self)>(\(value)) (\(scope.key))" - } - /// Removes the value from `iCloud` and resets the value to the inital value. public mutating func remove() { value = initial() diff --git a/Tests/AppStateTests/StoredStateTests.swift b/Tests/AppStateTests/StoredStateTests.swift index baafae5..c813bd7 100644 --- a/Tests/AppStateTests/StoredStateTests.swift +++ b/Tests/AppStateTests/StoredStateTests.swift @@ -45,6 +45,8 @@ final class StoredStateTests: XCTestCase { XCTAssertEqual(storedValue.count, 1) + Application.logger.debug("StoredStateTests \(Application.description)") + storedValue.count = nil XCTAssertNil(Application.storedState(\.storedValue).value) diff --git a/Tests/AppStateTests/SyncStateTests.swift b/Tests/AppStateTests/SyncStateTests.swift index bc709df..76c302a 100644 --- a/Tests/AppStateTests/SyncStateTests.swift +++ b/Tests/AppStateTests/SyncStateTests.swift @@ -52,6 +52,8 @@ final class SyncStateTests: XCTestCase { syncValue.count = 1 XCTAssertEqual(syncValue.count, 1) + + Application.logger.debug("SyncStateTests \(Application.description)") syncValue.count = nil