|
96 | 96 | where Self == AppStorageKey<Date> {
|
97 | 97 | AppStorageKey(key, store: store)
|
98 | 98 | }
|
| 99 | + |
| 100 | + /// Creates a shared key that can read and write a Codable value to user defaults. |
| 101 | + /// |
| 102 | + /// - Parameters: |
| 103 | + /// - key: The key to read and write the value to in the user defaults store. |
| 104 | + /// - store: The user defaults store to read and write to. A value of `nil` will use the user |
| 105 | + /// default store from dependencies. |
| 106 | + /// - Returns: A user defaults shared key. |
| 107 | + public static func appStorage<Value: Codable>( |
| 108 | + _ key: String, store: UserDefaults? = nil |
| 109 | + ) -> Self |
| 110 | + where Self == AppStorageKey<Value> { |
| 111 | + AppStorageKey(key, store: store) |
| 112 | + } |
99 | 113 |
|
100 | 114 | /// Creates a shared key that can read and write to an integer user default, transforming
|
101 | 115 | /// that to a `RawRepresentable` data type.
|
|
210 | 224 | where Self == AppStorageKey<Date?> {
|
211 | 225 | AppStorageKey(key, store: store)
|
212 | 226 | }
|
| 227 | + |
| 228 | + /// Creates a shared key that can read and write a Codable value to user defaults. |
| 229 | + /// |
| 230 | + /// - Parameters: |
| 231 | + /// - key: The key to read and write the value to in the user defaults store. |
| 232 | + /// - store: The user defaults store to read and write to. A value of `nil` will use the user |
| 233 | + /// default store from dependencies. |
| 234 | + /// - Returns: A user defaults shared key. |
| 235 | + public static func appStorage<Value: Codable>( |
| 236 | + _ key: String, store: UserDefaults? = nil |
| 237 | + ) -> Self |
| 238 | + where Self == AppStorageKey<Value?> { |
| 239 | + AppStorageKey(key, store: store) |
| 240 | + } |
213 | 241 |
|
214 | 242 | /// Creates a shared key that can read and write to an optional integer user default,
|
215 | 243 | /// transforming that to a `RawRepresentable` data type.
|
|
317 | 345 | fileprivate init(_ key: String, store: UserDefaults?) where Value == Date {
|
318 | 346 | self.init(lookup: CastableLookup(), key: key, store: store)
|
319 | 347 | }
|
| 348 | + |
| 349 | + fileprivate init(_ key: String, store: UserDefaults?) where Value: Codable { |
| 350 | + self.init(lookup: CodableLookup(), key: key, store: store) |
| 351 | + } |
320 | 352 |
|
321 | 353 | fileprivate init(_ key: String, store: UserDefaults?) where Value: RawRepresentable<Int> {
|
322 | 354 | self.init(lookup: RawRepresentableLookup(base: CastableLookup()), key: key, store: store)
|
|
353 | 385 | fileprivate init(_ key: String, store: UserDefaults?) where Value == Date? {
|
354 | 386 | self.init(lookup: OptionalLookup(base: CastableLookup()), key: key, store: store)
|
355 | 387 | }
|
| 388 | + |
| 389 | + fileprivate init<C: Codable>(_ key: String, store: UserDefaults?) |
| 390 | + where Value == C? { |
| 391 | + self.init( |
| 392 | + lookup: OptionalLookup(base: CodableLookup()), |
| 393 | + key: key, |
| 394 | + store: store |
| 395 | + ) |
| 396 | + } |
356 | 397 |
|
357 | 398 | fileprivate init<R: RawRepresentable<Int>>(_ key: String, store: UserDefaults?)
|
358 | 399 | where Value == R? {
|
|
602 | 643 | }
|
603 | 644 | }
|
604 | 645 |
|
| 646 | + private struct CodableLookup<Value: Codable & Sendable>: Lookup { |
| 647 | + func loadValue( |
| 648 | + from store: UserDefaults, |
| 649 | + at key: String, |
| 650 | + default defaultValue: Value? |
| 651 | + ) -> Value? { |
| 652 | + guard let data = store.data(forKey: key) |
| 653 | + else { |
| 654 | + guard !SharedAppStorageLocals.isSetting |
| 655 | + else { return nil } |
| 656 | + SharedAppStorageLocals.$isSetting.withValue(true) { |
| 657 | + if let value = defaultValue, let encoded = try? JSONEncoder().encode(value) { |
| 658 | + store.set(encoded, forKey: key) |
| 659 | + } |
| 660 | + } |
| 661 | + return defaultValue |
| 662 | + } |
| 663 | + return (try? JSONDecoder().decode(Value.self, from: data)) ?? defaultValue |
| 664 | + } |
| 665 | + |
| 666 | + func saveValue(_ newValue: Value, to store: UserDefaults, at key: String) { |
| 667 | + SharedAppStorageLocals.$isSetting.withValue(true) { |
| 668 | + if let encoded = try? JSONEncoder().encode(newValue) { |
| 669 | + store.set(encoded, forKey: key) |
| 670 | + } |
| 671 | + } |
| 672 | + } |
| 673 | + } |
| 674 | + |
605 | 675 | private struct URLLookup: Lookup {
|
606 | 676 | typealias Value = URL
|
607 | 677 |
|
|
0 commit comments