mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-04-13 08:38:00 +08:00
Move firebase encoder to separate file
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
CE7DD3831F9D04AE000225C5 /* FirestoreEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */; };
|
||||
CE7DD3841F9D04AE000225C5 /* FirestoreDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3821F9D04AE000225C5 /* FirestoreDecoder.swift */; };
|
||||
CE7DD3861F9DE4F7000225C5 /* TestCodableFirestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3851F9DE4F7000225C5 /* TestCodableFirestore.swift */; };
|
||||
CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFDBF811FF3B35B00745EBE /* Encoder.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -33,6 +34,7 @@
|
||||
CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirestoreEncoder.swift; sourceTree = "<group>"; };
|
||||
CE7DD3821F9D04AE000225C5 /* FirestoreDecoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirestoreDecoder.swift; sourceTree = "<group>"; };
|
||||
CE7DD3851F9DE4F7000225C5 /* TestCodableFirestore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestCodableFirestore.swift; sourceTree = "<group>"; };
|
||||
CEFDBF811FF3B35B00745EBE /* Encoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encoder.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -78,6 +80,7 @@
|
||||
CE7DD36A1F9CFA81000225C5 /* CodableFirebase.h */,
|
||||
CE7DD3821F9D04AE000225C5 /* FirestoreDecoder.swift */,
|
||||
CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */,
|
||||
CEFDBF811FF3B35B00745EBE /* Encoder.swift */,
|
||||
CE7DD36B1F9CFA81000225C5 /* Info.plist */,
|
||||
);
|
||||
path = CodableFirebase;
|
||||
@@ -204,6 +207,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CE7DD3831F9D04AE000225C5 /* FirestoreEncoder.swift in Sources */,
|
||||
CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */,
|
||||
CE7DD3841F9D04AE000225C5 /* FirestoreDecoder.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
494
CodableFirebase/Encoder.swift
Normal file
494
CodableFirebase/Encoder.swift
Normal file
@@ -0,0 +1,494 @@
|
||||
//
|
||||
// Encoder.swift
|
||||
// CodableFirebase
|
||||
//
|
||||
// Created by Oleksii on 27/12/2017.
|
||||
// Copyright © 2017 ViolentOctopus. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class _FirestoreEncoder : Encoder {
|
||||
fileprivate var storage: _FirestoreEncodingStorage
|
||||
fileprivate(set) public var codingPath: [CodingKey]
|
||||
public var userInfo: [CodingUserInfoKey : Any]
|
||||
|
||||
init(codingPath: [CodingKey] = []) {
|
||||
self.storage = _FirestoreEncodingStorage()
|
||||
self.codingPath = codingPath
|
||||
userInfo = [:]
|
||||
}
|
||||
|
||||
/// Returns whether a new element can be encoded at this coding path.
|
||||
///
|
||||
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
|
||||
fileprivate var canEncodeNewValue: Bool {
|
||||
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
|
||||
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
|
||||
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
|
||||
//
|
||||
// This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path.
|
||||
// Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here).
|
||||
return self.storage.count == self.codingPath.count
|
||||
}
|
||||
|
||||
// MARK: - Encoder Methods
|
||||
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
|
||||
// If an existing keyed container was already requested, return that one.
|
||||
let topContainer: NSMutableDictionary
|
||||
if canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = storage.pushKeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableDictionary else {
|
||||
preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public func unkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
// If an existing unkeyed container was already requested, return that one.
|
||||
let topContainer: NSMutableArray
|
||||
if canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = self.storage.pushUnkeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableArray else {
|
||||
preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
}
|
||||
|
||||
public func singleValueContainer() -> SingleValueEncodingContainer {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreEncodingStorage {
|
||||
// MARK: Properties
|
||||
/// The container stack.
|
||||
/// Elements may be any one of the plist types (NSNumber, NSString, NSDate, NSArray, NSDictionary).
|
||||
private(set) fileprivate var containers: [NSObject] = []
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with no containers.
|
||||
fileprivate init() {}
|
||||
|
||||
// MARK: - Modifying the Stack
|
||||
fileprivate var count: Int {
|
||||
return containers.count
|
||||
}
|
||||
|
||||
fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
|
||||
let dictionary = NSMutableDictionary()
|
||||
containers.append(dictionary)
|
||||
return dictionary
|
||||
}
|
||||
|
||||
fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
|
||||
let array = NSMutableArray()
|
||||
containers.append(array)
|
||||
return array
|
||||
}
|
||||
|
||||
fileprivate mutating func push(container: NSObject) {
|
||||
containers.append(container)
|
||||
}
|
||||
|
||||
fileprivate mutating func popContainer() -> NSObject {
|
||||
precondition(containers.count > 0, "Empty container stack.")
|
||||
return containers.popLast()!
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
|
||||
typealias Key = K
|
||||
|
||||
// MARK: Properties
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableDictionary
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||
public mutating func encodeNil(forKey key: Key) throws { container[key.stringValue] = NSNull() }
|
||||
public mutating func encode(_ value: Bool, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: String, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Float, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Double, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||
encoder.codingPath.append(key)
|
||||
defer { encoder.codingPath.removeLast() }
|
||||
container[key.stringValue] = try encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.container[key.stringValue] = dictionary
|
||||
|
||||
codingPath.append(key)
|
||||
defer { codingPath.removeLast() }
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: encoder, codingPath: codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
||||
let array = NSMutableArray()
|
||||
container[key.stringValue] = array
|
||||
|
||||
codingPath.append(key)
|
||||
defer { codingPath.removeLast() }
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: _FirebaseKey.super, wrapping: container)
|
||||
}
|
||||
|
||||
public mutating func superEncoder(forKey key: Key) -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: key, wrapping: container)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreUnkeyedEncodingContainer : UnkeyedEncodingContainer {
|
||||
// MARK: Properties
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableArray
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
/// The number of elements encoded into the container.
|
||||
public var count: Int {
|
||||
return container.count
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - UnkeyedEncodingContainer Methods
|
||||
public mutating func encodeNil() throws { container.add(NSNull()) }
|
||||
public mutating func encode(_ value: Bool) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int8) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int16) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int32) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int64) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt8) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt16) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt32) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt64) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Float) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Double) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: String) throws { container.add(self.encoder.box(value)) }
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T) throws {
|
||||
encoder.codingPath.append(_FirebaseKey(index: count))
|
||||
defer { encoder.codingPath.removeLast() }
|
||||
container.add(try encoder.box(value))
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
|
||||
self.codingPath.append(_FirebaseKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.container.add(dictionary)
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
self.codingPath.append(_FirebaseKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let array = NSMutableArray()
|
||||
self.container.add(array)
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: container.count, wrapping: container)
|
||||
}
|
||||
}
|
||||
|
||||
struct _FirebaseKey : CodingKey {
|
||||
public var stringValue: String
|
||||
public var intValue: Int?
|
||||
|
||||
public init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = nil
|
||||
}
|
||||
|
||||
public init?(intValue: Int) {
|
||||
self.stringValue = "\(intValue)"
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
init(index: Int) {
|
||||
self.stringValue = "Index \(index)"
|
||||
self.intValue = index
|
||||
}
|
||||
|
||||
static let `super` = _FirebaseKey(stringValue: "super")!
|
||||
}
|
||||
|
||||
extension _FirestoreEncoder {
|
||||
|
||||
/// Returns the given value boxed in a container appropriate for pushing onto the container stack.
|
||||
fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Float) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Double) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) }
|
||||
|
||||
fileprivate func box<T : Encodable>(_ value: T) throws -> NSObject {
|
||||
return try self.box_(value) ?? NSDictionary()
|
||||
}
|
||||
|
||||
func box_<T : Encodable>(_ value: T) throws -> NSObject? {
|
||||
if T.self == Date.self || T.self == NSDate.self {
|
||||
return (value as! NSDate)
|
||||
} else if T.self == Data.self || T.self == NSData.self {
|
||||
return (value as! NSData)
|
||||
}
|
||||
|
||||
// The value should request a container from the _FirestoreEncoder.
|
||||
let depth = storage.count
|
||||
try value.encode(to: self)
|
||||
|
||||
// The top container should be a new container.
|
||||
guard storage.count > depth else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return storage.popContainer()
|
||||
}
|
||||
}
|
||||
|
||||
extension _FirestoreEncoder : SingleValueEncodingContainer {
|
||||
// MARK: - SingleValueEncodingContainer Methods
|
||||
private func assertCanEncodeNewValue() {
|
||||
precondition(canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
|
||||
}
|
||||
|
||||
public func encodeNil() throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: NSNull())
|
||||
}
|
||||
|
||||
public func encode(_ value: Bool) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: String) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Float) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Double) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode<T : Encodable>(_ value: T) throws {
|
||||
assertCanEncodeNewValue()
|
||||
try storage.push(container: box(value))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class _FirestoreReferencingEncoder : _FirestoreEncoder {
|
||||
// MARK: Reference types.
|
||||
/// The type of container we're referencing.
|
||||
private enum Reference {
|
||||
/// Referencing a specific index in an array container.
|
||||
case array(NSMutableArray, Int)
|
||||
|
||||
/// Referencing a specific key in a dictionary container.
|
||||
case dictionary(NSMutableDictionary, String)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
/// The encoder we're referencing.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// The container reference itself.
|
||||
private let reference: Reference
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` by referencing the given array container in the given encoder.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, at index: Int, wrapping array: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.reference = .array(array, index)
|
||||
super.init(codingPath: encoder.codingPath)
|
||||
|
||||
self.codingPath.append(_FirebaseKey(index: index))
|
||||
}
|
||||
|
||||
/// Initializes `self` by referencing the given dictionary container in the given encoder.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
reference = .dictionary(dictionary, key.stringValue)
|
||||
super.init(codingPath: encoder.codingPath)
|
||||
codingPath.append(key)
|
||||
}
|
||||
|
||||
// MARK: - Coding Path Operations
|
||||
fileprivate override var canEncodeNewValue: Bool {
|
||||
// With a regular encoder, the storage and coding path grow together.
|
||||
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
|
||||
// We have to take this into account.
|
||||
return storage.count == codingPath.count - encoder.codingPath.count - 1
|
||||
}
|
||||
|
||||
// MARK: - Deinitialization
|
||||
// Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
|
||||
deinit {
|
||||
let value: Any
|
||||
switch storage.count {
|
||||
case 0: value = NSDictionary()
|
||||
case 1: value = self.storage.popContainer()
|
||||
default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
|
||||
}
|
||||
|
||||
switch self.reference {
|
||||
case .array(let array, let index):
|
||||
array.insert(value, at: index)
|
||||
|
||||
case .dictionary(let dictionary, let key):
|
||||
dictionary[NSString(string: key)] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension DecodingError {
|
||||
internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError {
|
||||
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
|
||||
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
|
||||
}
|
||||
|
||||
fileprivate static func _typeDescription(of value: Any) -> String {
|
||||
if value is NSNull {
|
||||
return "a null value"
|
||||
} else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ {
|
||||
return "a number"
|
||||
} else if value is String {
|
||||
return "a string/data"
|
||||
} else if value is [Any] {
|
||||
return "an array"
|
||||
} else if value is [String : Any] {
|
||||
return "a dictionary"
|
||||
} else {
|
||||
return "\(type(of: value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,489 +34,3 @@ open class FirestoreEncoder {
|
||||
return topLevel
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class _FirestoreEncoder : Encoder {
|
||||
fileprivate var storage: _FirestoreEncodingStorage
|
||||
fileprivate(set) public var codingPath: [CodingKey]
|
||||
public var userInfo: [CodingUserInfoKey : Any]
|
||||
|
||||
fileprivate init(codingPath: [CodingKey] = []) {
|
||||
self.storage = _FirestoreEncodingStorage()
|
||||
self.codingPath = codingPath
|
||||
userInfo = [:]
|
||||
}
|
||||
|
||||
/// Returns whether a new element can be encoded at this coding path.
|
||||
///
|
||||
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
|
||||
fileprivate var canEncodeNewValue: Bool {
|
||||
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
|
||||
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
|
||||
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
|
||||
//
|
||||
// This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path.
|
||||
// Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here).
|
||||
return self.storage.count == self.codingPath.count
|
||||
}
|
||||
|
||||
// MARK: - Encoder Methods
|
||||
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
|
||||
// If an existing keyed container was already requested, return that one.
|
||||
let topContainer: NSMutableDictionary
|
||||
if canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = storage.pushKeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableDictionary else {
|
||||
preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public func unkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
// If an existing unkeyed container was already requested, return that one.
|
||||
let topContainer: NSMutableArray
|
||||
if canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = self.storage.pushUnkeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableArray else {
|
||||
preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
}
|
||||
|
||||
public func singleValueContainer() -> SingleValueEncodingContainer {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreEncodingStorage {
|
||||
// MARK: Properties
|
||||
/// The container stack.
|
||||
/// Elements may be any one of the plist types (NSNumber, NSString, NSDate, NSArray, NSDictionary).
|
||||
private(set) fileprivate var containers: [NSObject] = []
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with no containers.
|
||||
fileprivate init() {}
|
||||
|
||||
// MARK: - Modifying the Stack
|
||||
fileprivate var count: Int {
|
||||
return containers.count
|
||||
}
|
||||
|
||||
fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
|
||||
let dictionary = NSMutableDictionary()
|
||||
containers.append(dictionary)
|
||||
return dictionary
|
||||
}
|
||||
|
||||
fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
|
||||
let array = NSMutableArray()
|
||||
containers.append(array)
|
||||
return array
|
||||
}
|
||||
|
||||
fileprivate mutating func push(container: NSObject) {
|
||||
containers.append(container)
|
||||
}
|
||||
|
||||
fileprivate mutating func popContainer() -> NSObject {
|
||||
precondition(containers.count > 0, "Empty container stack.")
|
||||
return containers.popLast()!
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
|
||||
typealias Key = K
|
||||
|
||||
// MARK: Properties
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableDictionary
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||
public mutating func encodeNil(forKey key: Key) throws { container[key.stringValue] = NSNull() }
|
||||
public mutating func encode(_ value: Bool, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Int64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt8, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt16, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt32, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: UInt64, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: String, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Float, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
public mutating func encode(_ value: Double, forKey key: Key) throws { container[key.stringValue] = encoder.box(value) }
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||
encoder.codingPath.append(key)
|
||||
defer { encoder.codingPath.removeLast() }
|
||||
container[key.stringValue] = try encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.container[key.stringValue] = dictionary
|
||||
|
||||
codingPath.append(key)
|
||||
defer { codingPath.removeLast() }
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: encoder, codingPath: codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
||||
let array = NSMutableArray()
|
||||
container[key.stringValue] = array
|
||||
|
||||
codingPath.append(key)
|
||||
defer { codingPath.removeLast() }
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: _FirebaseKey.super, wrapping: container)
|
||||
}
|
||||
|
||||
public mutating func superEncoder(forKey key: Key) -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: key, wrapping: container)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _FirestoreUnkeyedEncodingContainer : UnkeyedEncodingContainer {
|
||||
// MARK: Properties
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableArray
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
/// The number of elements encoded into the container.
|
||||
public var count: Int {
|
||||
return container.count
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - UnkeyedEncodingContainer Methods
|
||||
public mutating func encodeNil() throws { container.add(NSNull()) }
|
||||
public mutating func encode(_ value: Bool) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int8) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int16) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int32) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int64) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt8) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt16) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt32) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt64) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Float) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Double) throws { container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: String) throws { container.add(self.encoder.box(value)) }
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T) throws {
|
||||
encoder.codingPath.append(_FirebaseKey(index: count))
|
||||
defer { encoder.codingPath.removeLast() }
|
||||
container.add(try encoder.box(value))
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
|
||||
self.codingPath.append(_FirebaseKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.container.add(dictionary)
|
||||
|
||||
let container = _FirestoreKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
self.codingPath.append(_FirebaseKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let array = NSMutableArray()
|
||||
self.container.add(array)
|
||||
return _FirestoreUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return _FirestoreReferencingEncoder(referencing: encoder, at: container.count, wrapping: container)
|
||||
}
|
||||
}
|
||||
|
||||
struct _FirebaseKey : CodingKey {
|
||||
public var stringValue: String
|
||||
public var intValue: Int?
|
||||
|
||||
public init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = nil
|
||||
}
|
||||
|
||||
public init?(intValue: Int) {
|
||||
self.stringValue = "\(intValue)"
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
init(index: Int) {
|
||||
self.stringValue = "Index \(index)"
|
||||
self.intValue = index
|
||||
}
|
||||
|
||||
static let `super` = _FirebaseKey(stringValue: "super")!
|
||||
}
|
||||
|
||||
extension _FirestoreEncoder {
|
||||
|
||||
/// Returns the given value boxed in a container appropriate for pushing onto the container stack.
|
||||
fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Float) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Double) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) }
|
||||
|
||||
fileprivate func box<T : Encodable>(_ value: T) throws -> NSObject {
|
||||
return try self.box_(value) ?? NSDictionary()
|
||||
}
|
||||
|
||||
fileprivate func box_<T : Encodable>(_ value: T) throws -> NSObject? {
|
||||
if T.self == Date.self || T.self == NSDate.self {
|
||||
return (value as! NSDate)
|
||||
} else if T.self == Data.self || T.self == NSData.self {
|
||||
return (value as! NSData)
|
||||
}
|
||||
|
||||
// The value should request a container from the _FirestoreEncoder.
|
||||
let depth = storage.count
|
||||
try value.encode(to: self)
|
||||
|
||||
// The top container should be a new container.
|
||||
guard storage.count > depth else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return storage.popContainer()
|
||||
}
|
||||
}
|
||||
|
||||
extension _FirestoreEncoder : SingleValueEncodingContainer {
|
||||
// MARK: - SingleValueEncodingContainer Methods
|
||||
private func assertCanEncodeNewValue() {
|
||||
precondition(canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
|
||||
}
|
||||
|
||||
public func encodeNil() throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: NSNull())
|
||||
}
|
||||
|
||||
public func encode(_ value: Bool) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: String) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Float) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Double) throws {
|
||||
assertCanEncodeNewValue()
|
||||
storage.push(container: box(value))
|
||||
}
|
||||
|
||||
public func encode<T : Encodable>(_ value: T) throws {
|
||||
assertCanEncodeNewValue()
|
||||
try storage.push(container: box(value))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class _FirestoreReferencingEncoder : _FirestoreEncoder {
|
||||
// MARK: Reference types.
|
||||
/// The type of container we're referencing.
|
||||
private enum Reference {
|
||||
/// Referencing a specific index in an array container.
|
||||
case array(NSMutableArray, Int)
|
||||
|
||||
/// Referencing a specific key in a dictionary container.
|
||||
case dictionary(NSMutableDictionary, String)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
/// The encoder we're referencing.
|
||||
private let encoder: _FirestoreEncoder
|
||||
|
||||
/// The container reference itself.
|
||||
private let reference: Reference
|
||||
|
||||
// MARK: - Initialization
|
||||
/// Initializes `self` by referencing the given array container in the given encoder.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, at index: Int, wrapping array: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.reference = .array(array, index)
|
||||
super.init(codingPath: encoder.codingPath)
|
||||
|
||||
self.codingPath.append(_FirebaseKey(index: index))
|
||||
}
|
||||
|
||||
/// Initializes `self` by referencing the given dictionary container in the given encoder.
|
||||
fileprivate init(referencing encoder: _FirestoreEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
reference = .dictionary(dictionary, key.stringValue)
|
||||
super.init(codingPath: encoder.codingPath)
|
||||
codingPath.append(key)
|
||||
}
|
||||
|
||||
// MARK: - Coding Path Operations
|
||||
fileprivate override var canEncodeNewValue: Bool {
|
||||
// With a regular encoder, the storage and coding path grow together.
|
||||
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
|
||||
// We have to take this into account.
|
||||
return storage.count == codingPath.count - encoder.codingPath.count - 1
|
||||
}
|
||||
|
||||
// MARK: - Deinitialization
|
||||
// Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
|
||||
deinit {
|
||||
let value: Any
|
||||
switch storage.count {
|
||||
case 0: value = NSDictionary()
|
||||
case 1: value = self.storage.popContainer()
|
||||
default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
|
||||
}
|
||||
|
||||
switch self.reference {
|
||||
case .array(let array, let index):
|
||||
array.insert(value, at: index)
|
||||
|
||||
case .dictionary(let dictionary, let key):
|
||||
dictionary[NSString(string: key)] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension DecodingError {
|
||||
internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError {
|
||||
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
|
||||
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
|
||||
}
|
||||
|
||||
fileprivate static func _typeDescription(of value: Any) -> String {
|
||||
if value is NSNull {
|
||||
return "a null value"
|
||||
} else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ {
|
||||
return "a number"
|
||||
} else if value is String {
|
||||
return "a string/data"
|
||||
} else if value is [Any] {
|
||||
return "an array"
|
||||
} else if value is [String : Any] {
|
||||
return "a dictionary"
|
||||
} else {
|
||||
return "\(type(of: value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user