UserDefaultsValue.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //
  2. // UserDefaultsValue.swift
  3. // LoopFollow
  4. //
  5. // Created by Jon Fawcett on 6/4/20.
  6. // Copyright © 2020 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. /// A type that presents a key and a mutable value (type erased key-value)
  10. protocol UserDefaultsAnyValue {
  11. var key: String { get }
  12. var anyValue: Any? { get set }
  13. }
  14. /// A value holder that keeps its internal data (the value) synchronized with its UserDefaults value. The value is read from UserDefaults on initialization (if exists, otherwise a default value is used) and written when the value changes. Also a custom validation & onChange closure can be set on initialization.
  15. ///
  16. /// Another feature of this class is that when the value changes, the change can be observed by multiple observers. There are two observation levels: the instance level, when the observers register directly to the UserDefaultsValue instance, and group level, if the UserDefaultsValue instance is embeded in a group (UserDefaultsValueGroups class manages the groups). It is very convenient to declare related values in the same group, so when one of them changes, the group observers are notified that a change occured in that group (no need to observe each particular UserDefaultsValue instance).
  17. class UserDefaultsValue<T: AnyConvertible & Equatable> : UserDefaultsAnyValue {
  18. // user defaults key (UserDefaultsAnyValue protocol implementation)
  19. let key: String
  20. typealias ValueType = T
  21. // the value (strong typed)
  22. var value: T {
  23. didSet {
  24. // continue only if the new value is different than old value
  25. guard self.value != oldValue else {
  26. return
  27. }
  28. if let validation = validation {
  29. guard let validatedValue = validation(value) else {
  30. value = oldValue
  31. return
  32. }
  33. value = validatedValue
  34. }
  35. // store value to user defaults
  36. UserDefaultsValue.defaults.setValue(value.toAny(), forKey: key)
  37. // execute custom closure
  38. onChange?(value)
  39. // notify observers
  40. observers.values.forEach { $0(value) }
  41. // notify UserDefaultsValueGroups that value has changed
  42. UserDefaultsValueGroups.valueChanged(self)
  43. }
  44. }
  45. /// get/set the value from Any value (UserDefaultsAnyValue protocol implementation)
  46. var anyValue: Any? {
  47. get {
  48. return self.value.toAny()
  49. }
  50. set {
  51. guard let newValue = T.fromAny(newValue) as T? else {
  52. return
  53. }
  54. self.value = newValue
  55. }
  56. }
  57. /// is there this key already stored in UserDefaults?
  58. var exists: Bool {
  59. return UserDefaultsValue.defaults.object(forKey: key) != nil
  60. }
  61. // on change closure
  62. private let onChange: ((T) -> ())?
  63. // validate & transform closure : giving the new value, validate it; if validations passes, return the new value; if fails, transform the value, returning a modified version or ... return nil and the change will not gonna happen
  64. private let validation: ((T) -> T?)?
  65. // value change observers
  66. private var observers: [UUID : (T) -> Void] = [:]
  67. // user defaults used for persistence
  68. private class var defaults: UserDefaults {
  69. return UserDefaults(suiteName: AppConstants.APP_GROUP_ID)!
  70. }
  71. init(key: String, default defaultValue: T, onChange: ((T) -> Void)? = nil, validation: ((T) -> T?)? = nil) {
  72. self.key = key
  73. if let anyValue = UserDefaultsValue.defaults.object(forKey: key), let value = T.fromAny(anyValue) as T? {
  74. if let validation = validation {
  75. self.value = validation(value) ?? defaultValue
  76. } else {
  77. self.value = value
  78. }
  79. } else {
  80. self.value = defaultValue
  81. }
  82. self.onChange = onChange
  83. self.validation = validation
  84. }
  85. /// Insert this value in a group, useful for observing changes in the whole group, instead of particular values
  86. func group(_ groupName: String) -> Self {
  87. UserDefaultsValueGroups.add(self, to: groupName)
  88. return self
  89. }
  90. /// register observers, will be notified when value changes
  91. @discardableResult
  92. func observeChanges(using closure: @escaping(T) -> Void) -> ObservationToken {
  93. let id = UUID()
  94. observers[id] = closure
  95. return ObservationToken { [weak self] in
  96. self?.observers.removeValue(forKey: id)
  97. }
  98. }
  99. func setNil(key: String){
  100. UserDefaultsValue.defaults.removeObject(forKey: key)
  101. }
  102. }