ObservableUserDefaultsValue.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. //
  2. // ObservableUserDefaultsValue.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2024-07-24.
  6. // Copyright © 2024 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import Combine
  10. /// An observable value holder that keeps its internal data (the value) synchronized with its UserDefaults value.
  11. /// The value is read from UserDefaults on initialization (if exists, otherwise a default value is used) and written when the value changes.
  12. class ObservableUserDefaultsValue<T: AnyConvertible & Equatable>: ObservableObject, UserDefaultsAnyValue {
  13. // user defaults key (UserDefaultsAnyValue protocol implementation)
  14. let key: String
  15. typealias ValueType = T
  16. // The value (strong typed)
  17. @Published var value: T {
  18. didSet {
  19. // Continue only if the new value is different from the old value
  20. guard self.value != oldValue else { return }
  21. if let validation = validation {
  22. guard let validatedValue = validation(value) else {
  23. value = oldValue
  24. return
  25. }
  26. value = validatedValue
  27. }
  28. // Store value to user defaults
  29. ObservableUserDefaultsValue.defaults.setValue(value.toAny(), forKey: key)
  30. // Execute custom closure
  31. onChange?(value)
  32. // Notify observers
  33. observers.values.forEach { $0(value) }
  34. // Notify UserDefaultsValueGroups that value has changed
  35. UserDefaultsValueGroups.valueChanged(self)
  36. }
  37. }
  38. /// Get/set the value from Any value (UserDefaultsAnyValue protocol implementation)
  39. var anyValue: Any? {
  40. get {
  41. return self.value.toAny()
  42. }
  43. set {
  44. guard let newValue = T.fromAny(newValue) as T? else {
  45. return
  46. }
  47. self.value = newValue
  48. }
  49. }
  50. /// Is there this key already stored in UserDefaults?
  51. var exists: Bool {
  52. return ObservableUserDefaultsValue.defaults.object(forKey: key) != nil
  53. }
  54. // On change closure
  55. private let onChange: ((T) -> ())?
  56. // Validate & transform closure : given the new value, validate it; if validation passes, return the new value;
  57. // if validation fails, transform the value, returning a modified version or return nil and the change will not happen
  58. private let validation: ((T) -> T?)?
  59. // Value change observers
  60. private var observers: [UUID : (T) -> Void] = [:]
  61. // User defaults used for persistence
  62. private class var defaults: UserDefaults {
  63. return UserDefaults(suiteName: AppConstants.APP_GROUP_ID)!
  64. }
  65. init(key: String, default defaultValue: T, onChange: ((T) -> Void)? = nil, validation: ((T) -> T?)? = nil) {
  66. self.key = key
  67. self.onChange = onChange
  68. self.validation = validation
  69. if let anyValue = ObservableUserDefaultsValue.defaults.object(forKey: key), let value = T.fromAny(anyValue) as T? {
  70. self.value = validation?(value) ?? value
  71. } else {
  72. self.value = defaultValue
  73. }
  74. }
  75. /// Insert this value in a group, useful for observing changes in the whole group, instead of particular values
  76. func group(_ groupName: String) -> Self {
  77. UserDefaultsValueGroups.add(self, to: groupName)
  78. return self
  79. }
  80. /// Register observers, will be notified when value changes
  81. @discardableResult
  82. func observeChanges(using closure: @escaping (T) -> Void) -> ObservationToken {
  83. let id = UUID()
  84. observers[id] = closure
  85. return ObservationToken { [weak self] in
  86. self?.observers.removeValue(forKey: id)
  87. }
  88. }
  89. func setNil(key: String) {
  90. ObservableUserDefaultsValue.defaults.removeObject(forKey: key)
  91. }
  92. }