SecureStorageValue.swift 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. // LoopFollow
  2. // SecureStorageValue.swift
  3. import Combine
  4. import Foundation
  5. class SecureStorageValue<T: NSObject & NSSecureCoding & Equatable>: ObservableObject, BFUReloadable {
  6. let key: String
  7. @Published var value: T {
  8. didSet {
  9. guard value != oldValue else { return }
  10. // Memory-only during a suspect BFU window; see StorageValue's guard.
  11. guard !StorageReadiness.isSuppressingWrites else {
  12. StorageReadiness.noteSuppressedWrite(key: key)
  13. return
  14. }
  15. if let data = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true) {
  16. SecureStorageValue.defaults.set(data, forKey: key)
  17. }
  18. }
  19. }
  20. var exists: Bool {
  21. return SecureStorageValue.defaults.object(forKey: key) != nil
  22. }
  23. private static var defaults: UserDefaults {
  24. return UserDefaults.standard
  25. }
  26. init(key: String, defaultValue: T) {
  27. self.key = key
  28. if let data = SecureStorageValue.defaults.data(forKey: key),
  29. let decodedValue = try? NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: data)
  30. {
  31. value = decodedValue
  32. } else {
  33. value = defaultValue
  34. }
  35. StorageReadiness.register(self)
  36. }
  37. func remove() {
  38. SecureStorageValue.defaults.removeObject(forKey: key)
  39. }
  40. /// Re-reads from UserDefaults during BFU recovery. Same class-C backing as
  41. /// StorageValue, so it recovers identically.
  42. func reload() {
  43. if let data = SecureStorageValue.defaults.data(forKey: key),
  44. let decodedValue = try? NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: data),
  45. decodedValue != value
  46. {
  47. value = decodedValue
  48. }
  49. }
  50. }