ObservableUserDefaultsValue.swift 3.7 KB

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