Skip to content

Commit

Permalink
Improve logging and SyncState caching
Browse files Browse the repository at this point in the history
  • Loading branch information
0xLeif committed Nov 29, 2023
1 parent ac0c2c5 commit f33d023
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 24 deletions.
4 changes: 2 additions & 2 deletions Sources/AppState/Application/Application+public.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 16 additions & 1 deletion Sources/AppState/Application/Types/Application+State.swift
Original file line number Diff line number Diff line change
@@ -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<Value>: CustomStringConvertible {
enum StateType {
case state
case stored
case sync
}

private let type: StateType

/// A private backing storage for the value.
private var _value: Value

Expand All @@ -20,6 +28,7 @@ extension Application {
_value = newValue
shared.cache.set(
value: Application.State(
type: .state,
initial: newValue,
scope: scope
),
Expand All @@ -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))"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value>: CustomStringConvertible {
public struct StoredState<Value> {
@AppDependency(\.userDefaults) private var userDefaults: UserDefaults

/// The initial value of the state.
Expand Down Expand Up @@ -42,6 +42,7 @@ extension Application {
} else {
shared.cache.set(
value: Application.State(
type: .stored,
initial: newValue,
scope: scope
),
Expand Down Expand Up @@ -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()
Expand Down
29 changes: 13 additions & 16 deletions Sources/AppState/Application/Types/Application+SyncState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value: Codable>: CustomStringConvertible {
public struct SyncState<Value: Codable> {
@AppDependency(\.icloudStore) private var icloudStore: NSUbiquitousKeyValueStore

/// The initial value of the state.
Expand All @@ -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<Value>.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<Value>.self
)
else { return initial() }

return value
return cachedValue.value
}
set {
let mirror = Mirror(reflecting: newValue)
Expand All @@ -51,6 +51,7 @@ extension Application {
} else {
shared.cache.set(
value: Application.State(
type: .sync,
initial: newValue,
scope: scope
),
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions Tests/AppStateTests/StoredStateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions Tests/AppStateTests/SyncStateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ final class SyncStateTests: XCTestCase {
syncValue.count = 1

XCTAssertEqual(syncValue.count, 1)

Application.logger.debug("SyncStateTests \(Application.description)")

syncValue.count = nil

Expand Down

0 comments on commit f33d023

Please sign in to comment.