| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import Foundation
- /// The default app storage provider for apps which don't specify a
- /// custom one.
- ///
- /// This uses `UserDefaults` on all platforms.
- public typealias DefaultAppStorageProvider = UserDefaultsAppStorageProvider
- /// A type that can be used to persist ``AppStorage`` values to disk.
- public protocol AppStorageProvider: Sendable {
- /// Persists the given value.
- ///
- /// - Parameters:
- /// - value: The value to persist.
- /// - key: The key to assign the value to.
- func persistValue<Value: Codable>(_ value: Value, forKey key: String) throws
- /// Retrieves the value for the given key.
- ///
- /// - Parameters:
- /// - type: The type that you expect the value to be.
- /// - key: The key to retrieve the value from.
- /// - Returns: The persisted value for `key`, if it exists and is of the
- /// expected type; otherwise, `nil`.
- func retrieveValue<Value: Codable>(ofType type: Value.Type, forKey key: String) -> Value?
- }
- /// A simple app storage provider that uses `UserDefaults` to persist
- /// data.
- ///
- /// This works on all supported platforms.
- public struct UserDefaultsAppStorageProvider: AppStorageProvider {
- public func persistValue<Value: Codable>(_ value: Value, forKey key: String) throws {
- _ = try JSONEncoder().encode(value)
- // let jsonString = String(decoding: jsonData.bytes, as: UTF8.self)
- //UserDefaults.standard.set(jsonString, forKey: key)
- }
- public func retrieveValue<Value: Codable>(ofType: Value.Type, forKey key: String) -> Value? {
- return nil
- }
- }
- extension AppStorageProvider {
- public func getValue<T: Codable & Sendable>(key: String, defaultValue: T) -> T {
- // If this is the very first time we're reading from this key, it won't
- // be in the cache yet. In that case, we return the already-persisted value
- // if it exists, or the default value otherwise; either way, we add it to the
- // cache so subsequent accesses of `value` won't have to read from disk again.
- guard let cachedValue = appStorageCache[key] else {
- let value =
- self.retrieveValue(ofType: T.self, forKey: key) ?? defaultValue
- appStorageCache[key] = value
- return value
- }
- // Make sure that we have the right type.
- guard let typedValue = cachedValue as? T else {
- logger.warning(
- Logger.Message(stringLiteral: "'@AppStorage' property is of the wrong type; using default value"),
- metadata: [
- "key": "\(key)",
- "providedType": "\(T.self)",
- "actualType": "\(type(of: cachedValue))",
- ]
- )
- return defaultValue
- }
- return typedValue
- }
- public func setValue<T: Codable & Sendable>(key: String, newValue: T) {
- appStorageCache[key] = newValue
- do {
- logger.trace(Logger.Message(stringLiteral: "persisting '\(newValue)' for '\(key)'"))
- try self.persistValue(newValue, forKey: key)
- } catch {
- logger.warning(
- Logger.Message(stringLiteral: "failed to encode '@AppStorage' data"),
- metadata: [
- "value": "\(newValue)",
- "error": "\(error.localizedDescription)",
- ]
- )
- }
- }
- }
|