Skip to content

Commit

Permalink
add @LocalStorageEntry property wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaskollmer committed Jan 28, 2025
1 parent 6e8e6dd commit 6ca9e97
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Sources/SpeziLocalStorage/LocalStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public final class LocalStorage: Module, DefaultInitializable, EnvironmentAccess

try encryptedData.write(to: fileURL)
try setResourceValues()
key.publisher.send(value)
}


Expand Down Expand Up @@ -199,6 +200,7 @@ public final class LocalStorage: Module, DefaultInitializable, EnvironmentAccess
if fileManager.fileExists(atPath: fileURL.path) {
do {
try fileManager.removeItem(atPath: fileURL.path)
key.publisher.send(nil)
} catch {
throw LocalStorageError.deletionNotPossible
}
Expand Down
49 changes: 49 additions & 0 deletions Sources/SpeziLocalStorage/LocalStorageEntry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2025 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import Combine
import SwiftUI


/// Access ``LocalStorage`` entries within a SwiftUI View.
@propertyWrapper
public struct LocalStorageEntry<Value>: DynamicProperty {
private let key: LocalStorageKey<Value>

@Environment(LocalStorage.self) private var localStorage
@State private var internals = LocalStorageEntryInternals<Value>()

public init(_ key: LocalStorageKey<Value>) {
self.key = key
}

public func update() {
internals.subscribe(to: key, in: localStorage)
}

public var wrappedValue: Value? {
get { internals.value }
set { try? localStorage.store(newValue, for: key) }
}
}


@Observable private final class LocalStorageEntryInternals<Value> {
fileprivate var value: Value?

@ObservationIgnored
private var cancellable: AnyCancellable?

func subscribe(to key: LocalStorageKey<Value>, in localStorage: LocalStorage) {
cancellable?.cancel()
cancellable = key.publisher.sink { [weak self] newValue in
self?.value = newValue
}
value = try? localStorage.load(key)
}
}
5 changes: 3 additions & 2 deletions Sources/SpeziLocalStorage/LocalStorageKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Dispatch
import Combine
import Foundation
import SpeziFoundation

Expand Down Expand Up @@ -70,6 +70,7 @@ public final class LocalStorageKey<Value>: LocalStorageKeys, @unchecked Sendable
let encode: @Sendable (Value) throws -> Data
let decode: @Sendable (Data) throws -> Value?
private let lock = RWLock()
let publisher = PassthroughSubject<Value?, Never>()

/// Creates a Local Storage Key that uses custom encoding and decoding functions.
public init(
Expand Down Expand Up @@ -101,7 +102,7 @@ extension LocalStorageKey {
}

/// Creates a Local Storage Key that uses a custom encoder and decoder.
public convenience init<E: TopLevelEncoder & Sendable, D: TopLevelDecoder & Sendable>(
public convenience init<E: SpeziFoundation.TopLevelEncoder & Sendable, D: SpeziFoundation.TopLevelDecoder & Sendable>(
_ key: String,
setting: LocalStorageSetting = .default, // swiftlint:disable:this function_default_parameter_at_end
encoder: E,
Expand Down

0 comments on commit 6ca9e97

Please sign in to comment.