AppStorageProvider.swift 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import Foundation
  2. /// The default app storage provider for apps which don't specify a
  3. /// custom one.
  4. ///
  5. /// This uses `UserDefaults` on all platforms.
  6. public typealias DefaultAppStorageProvider = UserDefaultsAppStorageProvider
  7. /// A type that can be used to persist ``AppStorage`` values to disk.
  8. public protocol AppStorageProvider: Sendable {
  9. /// Persists the given value.
  10. ///
  11. /// - Parameters:
  12. /// - value: The value to persist.
  13. /// - key: The key to assign the value to.
  14. func persistValue<Value: Codable>(_ value: Value, forKey key: String) throws
  15. /// Retrieves the value for the given key.
  16. ///
  17. /// - Parameters:
  18. /// - type: The type that you expect the value to be.
  19. /// - key: The key to retrieve the value from.
  20. /// - Returns: The persisted value for `key`, if it exists and is of the
  21. /// expected type; otherwise, `nil`.
  22. func retrieveValue<Value: Codable>(ofType type: Value.Type, forKey key: String) -> Value?
  23. }
  24. /// A simple app storage provider that uses `UserDefaults` to persist
  25. /// data.
  26. ///
  27. /// This works on all supported platforms.
  28. public struct UserDefaultsAppStorageProvider: AppStorageProvider {
  29. public func persistValue<Value: Codable>(_ value: Value, forKey key: String) throws {
  30. _ = try JSONEncoder().encode(value)
  31. // let jsonString = String(decoding: jsonData.bytes, as: UTF8.self)
  32. //UserDefaults.standard.set(jsonString, forKey: key)
  33. }
  34. public func retrieveValue<Value: Codable>(ofType: Value.Type, forKey key: String) -> Value? {
  35. return nil
  36. }
  37. }
  38. extension AppStorageProvider {
  39. public func getValue<T: Codable & Sendable>(key: String, defaultValue: T) -> T {
  40. // If this is the very first time we're reading from this key, it won't
  41. // be in the cache yet. In that case, we return the already-persisted value
  42. // if it exists, or the default value otherwise; either way, we add it to the
  43. // cache so subsequent accesses of `value` won't have to read from disk again.
  44. guard let cachedValue = appStorageCache[key] else {
  45. let value =
  46. self.retrieveValue(ofType: T.self, forKey: key) ?? defaultValue
  47. appStorageCache[key] = value
  48. return value
  49. }
  50. // Make sure that we have the right type.
  51. guard let typedValue = cachedValue as? T else {
  52. logger.warning(
  53. Logger.Message(stringLiteral: "'@AppStorage' property is of the wrong type; using default value"),
  54. metadata: [
  55. "key": "\(key)",
  56. "providedType": "\(T.self)",
  57. "actualType": "\(type(of: cachedValue))",
  58. ]
  59. )
  60. return defaultValue
  61. }
  62. return typedValue
  63. }
  64. public func setValue<T: Codable & Sendable>(key: String, newValue: T) {
  65. appStorageCache[key] = newValue
  66. do {
  67. logger.trace(Logger.Message(stringLiteral: "persisting '\(newValue)' for '\(key)'"))
  68. try self.persistValue(newValue, forKey: key)
  69. } catch {
  70. logger.warning(
  71. Logger.Message(stringLiteral: "failed to encode '@AppStorage' data"),
  72. metadata: [
  73. "value": "\(newValue)",
  74. "error": "\(error.localizedDescription)",
  75. ]
  76. )
  77. }
  78. }
  79. }