ObservableUserDefaultsValue.swift 3.8 KB

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