FirestoreEncoder.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. // This file is derived from swift/stdlib/public/SDK/Foundation/JSONEncoder.swift
  2. // and swift/stdlib/public/SDK/Foundation/PlistEncoder.swift
  3. //===----------------------------------------------------------------------===//
  4. //
  5. // This source file is part of the Swift.org open source project
  6. //
  7. // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
  8. // Licensed under Apache License v2.0 with Runtime Library Exception
  9. //
  10. // See https://swift.org/LICENSE.txt for license information
  11. // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  12. //
  13. //===----------------------------------------------------------------------===//
  14. import FirebaseFirestore
  15. import Foundation
  16. extension Firestore {
  17. public struct Encoder {
  18. public init() {}
  19. /// Returns encoded data that Firestore API recognizes.
  20. ///
  21. /// If possible, all types will be converted to compatible types Firestore
  22. /// can handle. This means certain Firestore specific types will be encoded
  23. /// as pass-through: this encoder will only pass those types along since that
  24. /// is what Firestore can handle. The same types will be encoded differently
  25. /// with other encoders (for example: JSONEncoder).
  26. ///
  27. /// The Firestore pass-through types are:
  28. /// - GeoPoint
  29. /// - Timestamp
  30. /// - DocumentReference
  31. ///
  32. /// - Parameter value: The Encodable object to convert to encoded data.
  33. /// - Returns: A Map keyed by String representing a document Firestore
  34. /// API can work with.
  35. public func encode<T: Encodable>(_ value: T) throws -> [String: Any] {
  36. // DocumentReference and FieldValue cannot be encoded directly.
  37. guard T.self != DocumentReference.self, T.self != FieldValue.self else {
  38. throw FirestoreEncodingError.encodingIsNotSupported
  39. }
  40. guard let topLevel = try _FirestoreEncoder().box_(value) else {
  41. throw EncodingError.invalidValue(value,
  42. EncodingError.Context(codingPath: [],
  43. debugDescription: "Top-level \(T.self) did not encode any values."))
  44. }
  45. // This is O(n) check. Consider refactoring box_ to return [String: Any].
  46. guard let dict = topLevel as? [String: Any] else {
  47. throw EncodingError.invalidValue(value,
  48. EncodingError.Context(codingPath: [],
  49. debugDescription: "Top-level \(T.self) encoded not as dictionary."))
  50. }
  51. return dict
  52. }
  53. }
  54. }
  55. private class _FirestoreEncoder: Encoder {
  56. /// A stack of data containers storing encoded results. When a new object is being encoded,
  57. /// a corresponding storage is pushed to the stack; and when the field and all of its children objects
  58. /// are encoded, the stoage should have the entire encoded result for the sub-tree, it is then poped
  59. /// out and written to the proper place of the new stack top container by referencing to the top of `codingPath`.
  60. fileprivate var storage: _FirestoreEncodingStorage
  61. /// An array used as a stack to keep track of where in the encoded data tree the encoder is trying to process
  62. /// at the moment.
  63. public fileprivate(set) var codingPath: [CodingKey]
  64. public var userInfo: [CodingUserInfoKey: Any] = [:]
  65. init() {
  66. storage = _FirestoreEncodingStorage()
  67. codingPath = []
  68. }
  69. /// Returns whether we should allocate new container to store encoded result for the current
  70. /// codingPath.
  71. ///
  72. /// `true` if no container has been allocated for this coding path; `false` otherwise.
  73. fileprivate var shouldAllocateNewContainer: Bool {
  74. // The encoder starts with storage.count == codingPath.count == 0, which means it is encoding the root
  75. // object without any container allocated, so when a container is requested, it must be allocated
  76. // first. When a container is requested again with the same codingPath however, for another field from the root object possibly,
  77. // because now storage.count == codingPath + 1, we should not allocate new containers because there are
  78. // already encoded results in the container. This relation between the two stacks is maintained
  79. // throughout the encoding process.
  80. return storage.count == codingPath.count
  81. }
  82. // MARK: - Encoder Methods
  83. public func container<Key>(keyedBy _: Key.Type) -> KeyedEncodingContainer<Key> {
  84. // If an existing keyed container was already requested, return that one.
  85. let topContainer: NSMutableDictionary
  86. if shouldAllocateNewContainer {
  87. // We haven't yet pushed a container at this level; do so here.
  88. topContainer = storage.pushKeyedContainer()
  89. } else {
  90. guard let container = self.storage.containers.last as? NSMutableDictionary else {
  91. preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
  92. }
  93. topContainer = container
  94. }
  95. let container = _FirestoreKeyedEncodingContainer<Key>(referencing: self, codingPath: codingPath, wrapping: topContainer)
  96. return KeyedEncodingContainer(container)
  97. }
  98. public func unkeyedContainer() -> UnkeyedEncodingContainer {
  99. // If an existing unkeyed container was already requested, return that one.
  100. let topContainer: NSMutableArray
  101. if shouldAllocateNewContainer {
  102. // We haven't yet pushed a container at this level; do so here.
  103. topContainer = storage.pushUnkeyedContainer()
  104. } else {
  105. guard let container = self.storage.containers.last as? NSMutableArray else {
  106. preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
  107. }
  108. topContainer = container
  109. }
  110. return _FirestoreUnkeyedEncodingContainer(referencing: self, codingPath: codingPath, wrapping: topContainer)
  111. }
  112. public func singleValueContainer() -> SingleValueEncodingContainer {
  113. return self
  114. }
  115. }
  116. private struct _FirestoreEncodingStorage {
  117. // MARK: Properties
  118. /// The container stack.
  119. /// Elements may be any one of the plist types (NSNumber, NSString, NSDate, NSArray, NSDictionary).
  120. fileprivate private(set) var containers: [NSObject] = []
  121. // MARK: - Initialization
  122. /// Initializes `self` with no containers.
  123. fileprivate init() {}
  124. // MARK: - Modifying the Stack
  125. fileprivate var count: Int {
  126. return containers.count
  127. }
  128. fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
  129. let dictionary = NSMutableDictionary()
  130. containers.append(dictionary)
  131. return dictionary
  132. }
  133. fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
  134. let array = NSMutableArray()
  135. containers.append(array)
  136. return array
  137. }
  138. fileprivate mutating func push(container: NSObject) {
  139. containers.append(container)
  140. }
  141. fileprivate mutating func popContainer() -> NSObject {
  142. precondition(containers.count > 0, "Empty container stack.")
  143. let ret = containers.popLast()!
  144. return ret
  145. }
  146. }
  147. private struct _FirestoreKeyedEncodingContainer<K: CodingKey>: KeyedEncodingContainerProtocol {
  148. typealias Key = K
  149. // MARK: Properties
  150. /// A reference to the encoder we're writing to.
  151. private let encoder: _FirestoreEncoder
  152. /// A reference to the container we're writing to.
  153. private let container: NSMutableDictionary
  154. /// The path of coding keys taken to get to this point in encoding.
  155. public private(set) var codingPath: [CodingKey]
  156. // MARK: - Initialization
  157. /// Initializes `self` with the given references.
  158. fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
  159. self.encoder = encoder
  160. self.codingPath = codingPath
  161. self.container = container
  162. }
  163. // MARK: - KeyedEncodingContainerProtocol Methods
  164. public mutating func encodeNil(forKey key: Key) throws { container[key.stringValue] = NSNull() }
  165. public mutating func encode(_ value: Bool, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  166. public mutating func encode(_ value: Int, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  167. public mutating func encode(_ value: Int8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  168. public mutating func encode(_ value: Int16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  169. public mutating func encode(_ value: Int32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  170. public mutating func encode(_ value: Int64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  171. public mutating func encode(_ value: UInt, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  172. public mutating func encode(_ value: UInt8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  173. public mutating func encode(_ value: UInt16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  174. public mutating func encode(_ value: UInt32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  175. public mutating func encode(_ value: UInt64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  176. public mutating func encode(_ value: String, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  177. public mutating func encode(_ value: Float, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  178. public mutating func encode(_ value: Double, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
  179. public mutating func encode<T: Encodable>(_ value: T, forKey key: Key) throws {
  180. encoder.codingPath.append(key)
  181. defer {
  182. encoder.codingPath.removeLast()
  183. }
  184. container[key.stringValue] = try encoder.box(value)
  185. }
  186. public mutating func nestedContainer<NestedKey>(keyedBy _: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
  187. let dictionary = NSMutableDictionary()
  188. self.container[key.stringValue] = dictionary
  189. codingPath.append(key)
  190. defer {
  191. codingPath.removeLast()
  192. }
  193. let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: encoder, codingPath: codingPath, wrapping: dictionary)
  194. return KeyedEncodingContainer(container)
  195. }
  196. public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
  197. let array = NSMutableArray()
  198. container[key.stringValue] = array
  199. codingPath.append(key)
  200. defer {
  201. codingPath.removeLast()
  202. }
  203. return _FirestoreUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array)
  204. }
  205. public mutating func superEncoder() -> Encoder {
  206. return _FirestoreReferencingEncoder(referencing: encoder, at: _FirestoreKey.super, wrapping: container)
  207. }
  208. public mutating func superEncoder(forKey key: Key) -> Encoder {
  209. return _FirestoreReferencingEncoder(referencing: encoder, at: key, wrapping: container)
  210. }
  211. }
  212. private struct _FirestoreUnkeyedEncodingContainer: UnkeyedEncodingContainer {
  213. // MARK: Properties
  214. /// A reference to the encoder we're writing to.
  215. private let encoder: _FirestoreEncoder
  216. /// A reference to the container we're writing to.
  217. private let container: NSMutableArray
  218. /// The path of coding keys taken to get to this point in encoding.
  219. public private(set) var codingPath: [CodingKey]
  220. /// The number of elements encoded into the container.
  221. public var count: Int {
  222. return container.count
  223. }
  224. // MARK: - Initialization
  225. /// Initializes `self` with the given references.
  226. fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
  227. self.encoder = encoder
  228. self.codingPath = codingPath
  229. self.container = container
  230. }
  231. // MARK: - UnkeyedEncodingContainer Methods
  232. public mutating func encodeNil() throws { container.add(NSNull()) }
  233. public mutating func encode(_ value: Bool) throws { container.add(encoder.box(value)) }
  234. public mutating func encode(_ value: Int) throws { container.add(encoder.box(value)) }
  235. public mutating func encode(_ value: Int8) throws { container.add(encoder.box(value)) }
  236. public mutating func encode(_ value: Int16) throws { container.add(encoder.box(value)) }
  237. public mutating func encode(_ value: Int32) throws { container.add(encoder.box(value)) }
  238. public mutating func encode(_ value: Int64) throws { container.add(encoder.box(value)) }
  239. public mutating func encode(_ value: UInt) throws { container.add(encoder.box(value)) }
  240. public mutating func encode(_ value: UInt8) throws { container.add(encoder.box(value)) }
  241. public mutating func encode(_ value: UInt16) throws { container.add(encoder.box(value)) }
  242. public mutating func encode(_ value: UInt32) throws { container.add(encoder.box(value)) }
  243. public mutating func encode(_ value: UInt64) throws { container.add(encoder.box(value)) }
  244. public mutating func encode(_ value: Float) throws { container.add(encoder.box(value)) }
  245. public mutating func encode(_ value: Double) throws { container.add(encoder.box(value)) }
  246. public mutating func encode(_ value: String) throws { container.add(encoder.box(value)) }
  247. public mutating func encode<T: Encodable>(_ value: T) throws {
  248. encoder.codingPath.append(_FirestoreKey(index: count))
  249. defer { encoder.codingPath.removeLast()
  250. }
  251. container.add(try encoder.box(value))
  252. }
  253. public mutating func nestedContainer<NestedKey>(keyedBy _: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
  254. codingPath.append(_FirestoreKey(index: count))
  255. defer {
  256. self.codingPath.removeLast()
  257. }
  258. let dictionary = NSMutableDictionary()
  259. self.container.add(dictionary)
  260. let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: encoder, codingPath: codingPath, wrapping: dictionary)
  261. return KeyedEncodingContainer(container)
  262. }
  263. public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
  264. codingPath.append(_FirestoreKey(index: count))
  265. defer {
  266. self.codingPath.removeLast()
  267. }
  268. let array = NSMutableArray()
  269. container.add(array)
  270. return _FirestoreUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array)
  271. }
  272. public mutating func superEncoder() -> Encoder {
  273. return _FirestoreReferencingEncoder(referencing: encoder, at: container.count, wrapping: container)
  274. }
  275. }
  276. struct _FirestoreKey: CodingKey {
  277. public var stringValue: String
  278. public var intValue: Int?
  279. public init?(stringValue: String) {
  280. self.stringValue = stringValue
  281. intValue = nil
  282. }
  283. public init?(intValue: Int) {
  284. stringValue = "\(intValue)"
  285. self.intValue = intValue
  286. }
  287. init(index: Int) {
  288. stringValue = "Index \(index)"
  289. intValue = index
  290. }
  291. static let `super` = _FirestoreKey(stringValue: "super")!
  292. }
  293. extension _FirestoreEncoder {
  294. /// Returns the given value boxed in a container appropriate for pushing onto the container stack.
  295. fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) }
  296. fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) }
  297. fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) }
  298. fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) }
  299. fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) }
  300. fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) }
  301. fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) }
  302. fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) }
  303. fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) }
  304. fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) }
  305. fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) }
  306. fileprivate func box(_ value: Float) -> NSObject { return NSNumber(value: value) }
  307. fileprivate func box(_ value: Double) -> NSObject { return NSNumber(value: value) }
  308. fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) }
  309. fileprivate func box<T: Encodable>(_ value: T) throws -> NSObject {
  310. return try box_(value) ?? NSDictionary()
  311. }
  312. func box_<T: Encodable>(_ value: T) throws -> NSObject? {
  313. if T.self == Date.self || T.self == NSDate.self {
  314. return (value as! NSDate)
  315. } else if T.self == Data.self || T.self == NSData.self {
  316. return (value as! NSData)
  317. } else if T.self == URL.self || T.self == NSURL.self {
  318. return box((value as! URL).absoluteString)
  319. } else if T.self == Decimal.self || T.self == NSDecimalNumber.self {
  320. return (value as! NSDecimalNumber)
  321. } else if isFirestorePassthroughType(value) {
  322. // This is a native Firestore types that we don't need to encode.
  323. return (value as! NSObject)
  324. }
  325. // The value should request a container from the _FirestoreEncoder.
  326. let depth = storage.count
  327. do {
  328. try value.encode(to: self)
  329. } catch {
  330. // If the value pushed a container before throwing, pop it back off to restore state.
  331. if storage.count > depth {
  332. _ = storage.popContainer()
  333. }
  334. throw error
  335. }
  336. // The top container should be a new container.
  337. guard storage.count > depth else {
  338. return nil
  339. }
  340. return storage.popContainer()
  341. }
  342. }
  343. extension _FirestoreEncoder: SingleValueEncodingContainer {
  344. // MARK: - SingleValueEncodingContainer Methods
  345. private func assertCanEncodeNewValue() {
  346. precondition(shouldAllocateNewContainer, "Attempt to encode value through single value container when previously value already encoded.")
  347. }
  348. public func encodeNil() throws {
  349. assertCanEncodeNewValue()
  350. storage.push(container: NSNull())
  351. }
  352. public func encode(_ value: Bool) throws {
  353. assertCanEncodeNewValue()
  354. storage.push(container: box(value))
  355. }
  356. public func encode(_ value: Int) throws {
  357. assertCanEncodeNewValue()
  358. storage.push(container: box(value))
  359. }
  360. public func encode(_ value: Int8) throws {
  361. assertCanEncodeNewValue()
  362. storage.push(container: box(value))
  363. }
  364. public func encode(_ value: Int16) throws {
  365. assertCanEncodeNewValue()
  366. storage.push(container: box(value))
  367. }
  368. public func encode(_ value: Int32) throws {
  369. assertCanEncodeNewValue()
  370. storage.push(container: box(value))
  371. }
  372. public func encode(_ value: Int64) throws {
  373. assertCanEncodeNewValue()
  374. storage.push(container: box(value))
  375. }
  376. public func encode(_ value: UInt) throws {
  377. assertCanEncodeNewValue()
  378. storage.push(container: box(value))
  379. }
  380. public func encode(_ value: UInt8) throws {
  381. assertCanEncodeNewValue()
  382. storage.push(container: box(value))
  383. }
  384. public func encode(_ value: UInt16) throws {
  385. assertCanEncodeNewValue()
  386. storage.push(container: box(value))
  387. }
  388. public func encode(_ value: UInt32) throws {
  389. assertCanEncodeNewValue()
  390. storage.push(container: box(value))
  391. }
  392. public func encode(_ value: UInt64) throws {
  393. assertCanEncodeNewValue()
  394. storage.push(container: box(value))
  395. }
  396. public func encode(_ value: String) throws {
  397. assertCanEncodeNewValue()
  398. storage.push(container: box(value))
  399. }
  400. public func encode(_ value: Float) throws {
  401. assertCanEncodeNewValue()
  402. storage.push(container: box(value))
  403. }
  404. public func encode(_ value: Double) throws {
  405. assertCanEncodeNewValue()
  406. storage.push(container: box(value))
  407. }
  408. public func encode<T: Encodable>(_ value: T) throws {
  409. assertCanEncodeNewValue()
  410. try storage.push(container: box(value))
  411. }
  412. }
  413. /// Special subclass of `_FirestoreEncoder` used by `superEncoder`.
  414. /// It inherits the codingPath from the referencing `_FirestoreEncoder` but uses its own
  415. /// storage. The encoded result will be written back to the referencing encoder's storage
  416. /// when it is `deinit`-ed.
  417. private class _FirestoreReferencingEncoder: _FirestoreEncoder {
  418. // MARK: Reference types.
  419. /// The type of container we're referencing, and where to write the encoded result to.
  420. private enum Reference {
  421. /// Referencing a specific index in an array container.
  422. case array(NSMutableArray, Int)
  423. /// Referencing a specific key in a dictionary container.
  424. case dictionary(NSMutableDictionary, String)
  425. }
  426. // MARK: - Properties
  427. /// The encoder we're referencing.
  428. private let encoder: _FirestoreEncoder
  429. /// The container reference itself.
  430. private let reference: Reference
  431. // MARK: - Initialization
  432. /// Initializes `self` by referencing the given array container in the given encoder.
  433. fileprivate init(referencing encoder: _FirestoreEncoder, at index: Int, wrapping array: NSMutableArray) {
  434. self.encoder = encoder
  435. reference = .array(array, index)
  436. super.init()
  437. codingPath = encoder.codingPath
  438. codingPath.append(_FirestoreKey(index: index))
  439. }
  440. /// Initializes `self` by referencing the given dictionary container in the given encoder.
  441. fileprivate init(referencing encoder: _FirestoreEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
  442. self.encoder = encoder
  443. reference = .dictionary(dictionary, key.stringValue)
  444. super.init()
  445. codingPath = encoder.codingPath
  446. codingPath.append(key)
  447. }
  448. // MARK: - Coding Path Operations
  449. fileprivate override var shouldAllocateNewContainer: Bool {
  450. // With a regular encoder, the storage and coding path grow together.
  451. // A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
  452. // We have to take this into account.
  453. return storage.count == codingPath.count - encoder.codingPath.count - 1
  454. }
  455. // MARK: - Deinitialization
  456. // Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
  457. deinit {
  458. let value: Any
  459. switch storage.count {
  460. case 0: value = NSDictionary()
  461. case 1: value = self.storage.popContainer()
  462. default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
  463. }
  464. switch self.reference {
  465. case let .array(array, index):
  466. array.insert(value, at: index)
  467. case let .dictionary(dictionary, key):
  468. dictionary[NSString(string: key)] = value
  469. }
  470. }
  471. }