ObservableUserDefaultsValue.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // LoopFollow
  2. // ObservableUserDefaultsValue.swift
  3. import Combine
  4. import Foundation
  5. class ObservableUserDefaultsValue<T: AnyConvertible & Equatable>: ObservableObject, UserDefaultsAnyValue {
  6. // user defaults key (UserDefaultsAnyValue protocol implementation)
  7. let key: String
  8. typealias ValueType = T
  9. // The value (strong typed)
  10. @Published var value: T {
  11. didSet {
  12. // Continue only if the new value is different from the old value
  13. guard value != oldValue else { return }
  14. if let validation = validation {
  15. guard let validatedValue = validation(value) else {
  16. DispatchQueue.main.async {
  17. self.value = oldValue
  18. }
  19. return
  20. }
  21. value = validatedValue
  22. }
  23. // Store value to user defaults
  24. ObservableUserDefaultsValue.defaults.setValue(value.toAny(), forKey: key)
  25. // Execute custom closure
  26. DispatchQueue.main.async {
  27. self.onChange?(self.value)
  28. // Notify observers
  29. self.observers.values.forEach { $0(self.value) }
  30. // Notify UserDefaultsValueGroups that value has changed
  31. UserDefaultsValueGroups.valueChanged(self)
  32. print("Value for \(self.key) changed to \(self.value)") // Logging
  33. }
  34. }
  35. }
  36. /// Get/set the value from Any value (UserDefaultsAnyValue protocol implementation)
  37. var anyValue: Any? {
  38. get {
  39. return value.toAny()
  40. }
  41. set {
  42. guard let newValue = T.fromAny(newValue) as T? else {
  43. return
  44. }
  45. DispatchQueue.main.async {
  46. self.value = newValue
  47. }
  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) -> Void)?
  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. 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. }