FirestoreEncoder.swift 23 KB

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