DefaultsBridges.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. //
  2. // SwiftyUserDefaults
  3. //
  4. // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in all
  14. // copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. // SOFTWARE.
  23. //
  24. import Foundation
  25. public protocol DefaultsBridge {
  26. associatedtype T
  27. /// This method provides a way of saving your data in UserDefaults. Usually needed
  28. /// when you want to create your custom Bridge, so you'll have to override it.
  29. func get(key: String, userDefaults: UserDefaults) -> T?
  30. /// This method provides a way of saving your data in UserDefaults. Usually needed
  31. /// when you want to create your custom Bridge, so you'll have to override it.
  32. func save(key: String, value: T?, userDefaults: UserDefaults)
  33. /// Override this function if your data is represented differently in UserDefaults
  34. /// and you map it in save/get methods.
  35. ///
  36. /// For instance, if you store it as Data in UserDefaults, but your type is not Data in your
  37. /// defaults key, then you need to provide `deserialize(_:)` method as well.
  38. ///
  39. /// Similar if you store your array of type as e.g. `[String]` but the type you use is actually `[SomeClassThatHasOnlyOneStringProperty]`.
  40. ///
  41. /// See `DefaultsRawRepresentableBridge` or `DefaultsCodableBridge` for examples.
  42. func deserialize(_ object: Any) -> T?
  43. }
  44. public struct DefaultsObjectBridge<T>: DefaultsBridge {
  45. public init() {}
  46. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  47. userDefaults.set(value, forKey: key)
  48. }
  49. public func get(key: String, userDefaults: UserDefaults) -> T? {
  50. return userDefaults.object(forKey: key) as? T
  51. }
  52. public func deserialize(_ object: Any) -> T? {
  53. return nil
  54. }
  55. }
  56. public struct DefaultsArrayBridge<T: Collection>: DefaultsBridge {
  57. public init() {}
  58. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  59. userDefaults.set(value, forKey: key)
  60. }
  61. public func get(key: String, userDefaults: UserDefaults) -> T? {
  62. return userDefaults.array(forKey: key) as? T
  63. }
  64. public func deserialize(_ object: Any) -> T? {
  65. return nil
  66. }
  67. }
  68. public struct DefaultsStringBridge: DefaultsBridge {
  69. public init() {}
  70. public func save(key: String, value: String?, userDefaults: UserDefaults) {
  71. userDefaults.set(value, forKey: key)
  72. }
  73. public func get(key: String, userDefaults: UserDefaults) -> String? {
  74. return userDefaults.string(forKey: key)
  75. }
  76. public func deserialize(_ object: Any) -> String? {
  77. return nil
  78. }
  79. }
  80. public struct DefaultsIntBridge: DefaultsBridge {
  81. public init() {}
  82. public func save(key: String, value: Int?, userDefaults: UserDefaults) {
  83. userDefaults.set(value, forKey: key)
  84. }
  85. public func get(key: String, userDefaults: UserDefaults) -> Int? {
  86. if let int = userDefaults.number(forKey: key)?.intValue {
  87. return int
  88. }
  89. // Fallback for launch arguments
  90. if let string = userDefaults.object(forKey: key) as? String,
  91. let int = Int(string) {
  92. return int
  93. }
  94. return nil
  95. }
  96. public func deserialize(_ object: Any) -> Int? {
  97. return nil
  98. }
  99. }
  100. public struct DefaultsDoubleBridge: DefaultsBridge {
  101. public init() {}
  102. public func save(key: String, value: Double?, userDefaults: UserDefaults) {
  103. userDefaults.set(value, forKey: key)
  104. }
  105. public func get(key: String, userDefaults: UserDefaults) -> Double? {
  106. if let double = userDefaults.number(forKey: key)?.doubleValue {
  107. return double
  108. }
  109. // Fallback for launch arguments
  110. if let string = userDefaults.object(forKey: key) as? String,
  111. let double = Double(string) {
  112. return double
  113. }
  114. return nil
  115. }
  116. public func deserialize(_ object: Any) -> Double? {
  117. return nil
  118. }
  119. }
  120. public struct DefaultsBoolBridge: DefaultsBridge {
  121. public init() {}
  122. public func save(key: String, value: Bool?, userDefaults: UserDefaults) {
  123. userDefaults.set(value, forKey: key)
  124. }
  125. public func get(key: String, userDefaults: UserDefaults) -> Bool? {
  126. // @warning we use number(forKey:) instead of bool(forKey:), because
  127. // bool(forKey:) will always return a value, even if it's not set
  128. //
  129. // Now, let's see if there is value in defaults that converts to Bool first:
  130. if let bool = userDefaults.number(forKey: key)?.boolValue {
  131. return bool
  132. }
  133. // If not, fallback for values saved in a plist (e.g. for testing)
  134. // For instance, few of the string("YES", "true", "NO", "false") convert to Bool from a property list
  135. return (userDefaults.object(forKey: key) as? String)?.bool
  136. }
  137. public func deserialize(_ object: Any) -> Bool? {
  138. return nil
  139. }
  140. }
  141. public struct DefaultsDataBridge: DefaultsBridge {
  142. public init() {}
  143. public func save(key: String, value: Data?, userDefaults: UserDefaults) {
  144. userDefaults.set(value, forKey: key)
  145. }
  146. public func get(key: String, userDefaults: UserDefaults) -> Data? {
  147. return userDefaults.data(forKey: key)
  148. }
  149. public func deserialize(_ object: Any) -> Data? {
  150. return nil
  151. }
  152. }
  153. public struct DefaultsUrlBridge: DefaultsBridge {
  154. public init() {}
  155. public func save(key: String, value: URL?, userDefaults: UserDefaults) {
  156. userDefaults.set(value, forKey: key)
  157. }
  158. public func get(key: String, userDefaults: UserDefaults) -> URL? {
  159. return userDefaults.url(forKey: key)
  160. }
  161. public func deserialize(_ object: Any) -> URL? {
  162. if let object = object as? URL {
  163. return object
  164. }
  165. if let object = object as? Data {
  166. return NSKeyedUnarchiver.unarchiveObject(with: object) as? URL
  167. }
  168. if let object = object as? NSString {
  169. let path = object.expandingTildeInPath
  170. return URL(fileURLWithPath: path)
  171. }
  172. return nil
  173. }
  174. }
  175. public struct DefaultsCodableBridge<T: Codable>: DefaultsBridge {
  176. public init() {}
  177. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  178. guard let value = value else {
  179. userDefaults.removeObject(forKey: key)
  180. return
  181. }
  182. userDefaults.set(encodable: value, forKey: key)
  183. }
  184. public func get(key: String, userDefaults: UserDefaults) -> T? {
  185. guard let data = userDefaults.data(forKey: key) else {
  186. return nil
  187. }
  188. return deserialize(data)
  189. }
  190. public func deserialize(_ object: Any) -> T? {
  191. guard let data = object as? Data else { return nil }
  192. return try? JSONDecoder().decode(T.self, from: data)
  193. }
  194. }
  195. public struct DefaultsKeyedArchiverBridge<T>: DefaultsBridge {
  196. public init() {}
  197. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  198. guard let value = value else {
  199. userDefaults.removeObject(forKey: key)
  200. return
  201. }
  202. // Needed because Quick/Nimble have min target 10.10...
  203. if #available(OSX 10.11, *) {
  204. userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key)
  205. } else {
  206. fatalError("Shouldn't really happen. We do not support macOS 10.10, if it happened to you please report your use-case on GitHub issues.")
  207. }
  208. }
  209. public func get(key: String, userDefaults: UserDefaults) -> T? {
  210. guard let data = userDefaults.data(forKey: key) else {
  211. return nil
  212. }
  213. return deserialize(data)
  214. }
  215. public func deserialize(_ object: Any) -> T? {
  216. guard let data = object as? Data else { return nil }
  217. return NSKeyedUnarchiver.unarchiveObject(with: data) as? T
  218. }
  219. }
  220. public struct DefaultsRawRepresentableBridge<T: RawRepresentable>: DefaultsBridge {
  221. public init() {}
  222. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  223. userDefaults.set(value?.rawValue, forKey: key)
  224. }
  225. public func get(key: String, userDefaults: UserDefaults) -> T? {
  226. guard let object = userDefaults.object(forKey: key) else { return nil }
  227. return deserialize(object)
  228. }
  229. public func deserialize(_ object: Any) -> T? {
  230. guard let rawValue = object as? T.RawValue else { return nil }
  231. return T(rawValue: rawValue)
  232. }
  233. }
  234. public struct DefaultsRawRepresentableArrayBridge<T: Collection>: DefaultsBridge where T.Element: RawRepresentable {
  235. public init() {}
  236. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  237. let raw = value?.map { $0.rawValue }
  238. userDefaults.set(raw, forKey: key)
  239. }
  240. public func get(key: String, userDefaults: UserDefaults) -> T? {
  241. guard let object = userDefaults.array(forKey: key) else { return nil }
  242. return deserialize(object)
  243. }
  244. public func deserialize(_ object: Any) -> T? {
  245. guard let rawValue = object as? [T.Element.RawValue] else { return nil }
  246. return rawValue.compactMap { T.Element(rawValue: $0) } as? T
  247. }
  248. }
  249. public struct DefaultsOptionalBridge<Bridge: DefaultsBridge>: DefaultsBridge {
  250. public typealias T = Bridge.T?
  251. private let bridge: Bridge
  252. init(bridge: Bridge) {
  253. self.bridge = bridge
  254. }
  255. public func get(key: String, userDefaults: UserDefaults) -> T? {
  256. return bridge.get(key: key, userDefaults: userDefaults)
  257. }
  258. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  259. bridge.save(key: key, value: value as? Bridge.T, userDefaults: userDefaults)
  260. }
  261. public func deserialize(_ object: Any) -> T? {
  262. return bridge.deserialize(object)
  263. }
  264. }
  265. public struct DefaultsOptionalArrayBridge<Bridge: DefaultsBridge>: DefaultsBridge where Bridge.T: Collection {
  266. public typealias T = Bridge.T?
  267. private let bridge: Bridge
  268. init(bridge: Bridge) {
  269. self.bridge = bridge
  270. }
  271. public func get(key: String, userDefaults: UserDefaults) -> T? {
  272. return bridge.get(key: key, userDefaults: userDefaults)
  273. }
  274. public func save(key: String, value: T?, userDefaults: UserDefaults) {
  275. bridge.save(key: key, value: value as? Bridge.T, userDefaults: userDefaults)
  276. }
  277. public func deserialize(_ object: Any) -> T? {
  278. return bridge.deserialize(object)
  279. }
  280. }