ObservableUserDefaultsValue.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. }
  33. }
  34. }
  35. /// Get/set the value from Any value (UserDefaultsAnyValue protocol implementation)
  36. var anyValue: Any? {
  37. get {
  38. return value.toAny()
  39. }
  40. set {
  41. guard let newValue = T.fromAny(newValue) as T? else {
  42. return
  43. }
  44. DispatchQueue.main.async {
  45. self.value = newValue
  46. }
  47. }
  48. }
  49. /// Is there this key already stored in UserDefaults?
  50. var exists: Bool {
  51. return ObservableUserDefaultsValue.defaults.object(forKey: key) != nil
  52. }
  53. // On change closure
  54. private let onChange: ((T) -> Void)?
  55. // Validate & transform closure : given the new value, validate it; if validation passes, return the new value;
  56. // if validation fails, transform the value, returning a modified version or return nil and the change will not happen
  57. private let validation: ((T) -> T?)?
  58. // Value change observers
  59. private var observers: [UUID: (T) -> Void] = [:]
  60. // User defaults used for persistence
  61. private class var defaults: UserDefaults {
  62. return UserDefaults(suiteName: AppConstants.APP_GROUP_ID)!
  63. }
  64. init(key: String, default defaultValue: T, onChange: ((T) -> Void)? = nil, validation: ((T) -> T?)? = nil) {
  65. self.key = key
  66. self.onChange = onChange
  67. self.validation = validation
  68. if let anyValue = ObservableUserDefaultsValue.defaults.object(forKey: key), let value = T.fromAny(anyValue) as T? {
  69. self.value = validation?(value) ?? value
  70. } else {
  71. value = defaultValue
  72. }
  73. }
  74. /// Insert this value in a group, useful for observing changes in the whole group, instead of particular values
  75. func group(_ groupName: String) -> Self {
  76. UserDefaultsValueGroups.add(self, to: groupName)
  77. return self
  78. }
  79. /// Register observers, will be notified when value changes
  80. @discardableResult
  81. func observeChanges(using closure: @escaping (T) -> Void) -> ObservationToken {
  82. let id = UUID()
  83. observers[id] = closure
  84. return ObservationToken { [weak self] in
  85. self?.observers.removeValue(forKey: id)
  86. }
  87. }
  88. func setNil(key: String) {
  89. ObservableUserDefaultsValue.defaults.removeObject(forKey: key)
  90. }
  91. }