2019-06-19 10:00:17 -04:00
// This file is derived from swift/stdlib/public/SDK/Foundation/JSONEncoder.swift
// and swift/stdlib/public/SDK/Foundation/PlistEncoder.swift
2019-09-26 11:03:05 -07:00
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
2019-06-19 10:00:17 -04:00
import FirebaseFirestore
import Foundation
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
extension Firestore {
public struct Decoder {
2019-09-06 09:26:59 -04:00
fileprivate static let documentRefUserInfoKey = CodingUserInfoKey ( rawValue : "DocumentRefUserInfoKey" )
2019-09-26 11:03:05 -07:00
public init () {}
2019-06-19 10:00:17 -04:00
/// Returns an instance of specified type from a Firestore document.
2019-09-26 11:03:05 -07:00
///
2019-06-19 10:00:17 -04:00
/// If exists in `container`, Firestore specific types are recognized, and
/// passed through to `Decodable` implementations. This means types below
/// in `container` are directly supported:
/// - GeoPoint
/// - Timestamp
2019-06-28 15:07:52 -04:00
/// - DocumentReference
2019-09-26 11:03:05 -07:00
///
2019-06-19 10:00:17 -04:00
/// - Parameters:
/// - A type to decode a document to.
/// - container: A Map keyed of String representing a Firestore document.
2019-09-06 09:26:59 -04:00
/// - document: A reference to the Firestore Document that is being
/// decoded.
2019-06-19 10:00:17 -04:00
/// - Returns: An instance of specified type by the first parameter.
2019-09-06 09:26:59 -04:00
public func decode < T : Decodable >( _ : T . Type ,
from container : [ String : Any ],
in document : DocumentReference ? = nil ) throws -> T {
2019-06-19 10:00:17 -04:00
let decoder = _FirestoreDecoder ( referencing : container )
2019-09-06 09:26:59 -04:00
if let doc = document {
decoder . userInfo [ Firestore . Decoder . documentRefUserInfoKey !] = doc
}
2019-06-19 10:00:17 -04:00
guard let value = try decoder . unbox ( container , as : T . self ) else {
throw DecodingError . valueNotFound (
T . self ,
DecodingError . Context ( codingPath : [],
debugDescription : "The given dictionary was invalid" )
)
}
return value
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
class _FirestoreDecoder : Decoder {
// `_FirestoreDecoder`
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
// MARK: Properties
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// A stack of data containers storing data containers to decode. When a new data
/// container is being decoded, a corresponding storage is pushed to the stack;
/// and when that container (and all of its children containers) has been decoded,
/// it is popped out such that the decoding can proceed with new top storage.
fileprivate var storage : _FirestoreDecodingStorage
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// The path to the current point in the container tree. Given the root container, one could
/// conceptually reconstruct `storage` by following `codingPath` from the root container.
public fileprivate ( set ) var codingPath : [ CodingKey ]
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// Contextual user-provided information for use during encoding.
public var userInfo : [ CodingUserInfoKey : Any ] = [:]
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
// MARK: - Initialization
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// Initializes `self` with the given top-level container and options.
init ( referencing container : Any , at codingPath : [ CodingKey ] = []) {
storage = _FirestoreDecodingStorage ()
storage . push ( container : container )
self . codingPath = codingPath
}
// MARK: - Decoder Methods
public func container < Key >( keyedBy _ : Key . Type ) throws -> KeyedDecodingContainer < Key > {
guard ! ( storage . topContainer is NSNull ) else {
throw DecodingError . valueNotFound ( KeyedDecodingContainer < Key >. self ,
DecodingError . Context ( codingPath : codingPath ,
debugDescription : "Cannot get keyed decoding container -- found null value instead." ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
guard let topContainer = self . storage . topContainer as ? [ String : Any ] else {
let context = DecodingError . Context ( codingPath : codingPath , debugDescription : "Not a dictionary" )
throw DecodingError . typeMismatch ([ String : Any ]. self , context )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let container = _FirestoreKeyedDecodingContainer < Key >( referencing : self , wrapping : topContainer )
return KeyedDecodingContainer ( container )
}
public func unkeyedContainer () throws -> UnkeyedDecodingContainer {
guard ! ( storage . topContainer is NSNull ) else {
throw DecodingError . valueNotFound ( UnkeyedDecodingContainer . self ,
DecodingError . Context ( codingPath : codingPath ,
debugDescription : "Cannot get unkeyed decoding container -- found null value instead." ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
guard let topContainer = self . storage . topContainer as ? [ Any ] else {
let context = DecodingError . Context ( codingPath : codingPath , debugDescription : "Not an array" )
throw DecodingError . typeMismatch ([ Any ]. self , context )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
return _FirestoreUnkeyedDecodingContainer ( referencing : self , wrapping : topContainer )
}
public func singleValueContainer () throws -> SingleValueDecodingContainer {
return self
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
private struct _FirestoreDecodingStorage {
// MARK: Properties
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// The container stack.
/// Elements may be any one of the plist types (NSNumber, Date, String, Array, [String : Any]).
fileprivate private ( set ) var containers : [ Any ] = []
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
// MARK: - Initialization
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// Initializes `self` with no containers.
fileprivate init () {}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
// MARK: - Modifying the Stack
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
fileprivate var count : Int {
return containers . count
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
fileprivate var topContainer : Any {
precondition ( containers . count > 0 , "Empty container stack." )
return containers . last !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
fileprivate mutating func push ( container : Any ) {
containers . append ( container )
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
fileprivate mutating func popContainer () {
precondition ( containers . count > 0 , "Empty container stack." )
containers . removeLast ()
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
private struct _FirestoreKeyedDecodingContainer < K : CodingKey >: KeyedDecodingContainerProtocol {
typealias Key = K
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
// MARK: Properties
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// A reference to the decoder we're reading from.
private let decoder : _FirestoreDecoder
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// A reference to the container we're reading from.
private let container : [ String : Any ]
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// The path of coding keys taken to get to this point in decoding.
public private ( set ) var codingPath : [ CodingKey ]
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
fileprivate init ( referencing decoder : _FirestoreDecoder , wrapping container : [ String : Any ]) {
self . decoder = decoder
self . container = container
codingPath = decoder . codingPath
}
// MARK: - KeyedDecodingContainerProtocol Methods
public var allKeys : [ Key ] {
return container . keys . compactMap { Key ( stringValue : $0 ) }
}
public func contains ( _ key : Key ) -> Bool {
return container [ key . stringValue ] != nil
}
public func decodeNil ( forKey key : Key ) throws -> Bool {
let entry = try require ( key : key )
return entry is NSNull
}
public func decode ( _ : Bool . Type , forKey key : Key ) throws -> Bool {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Bool . self )
return try require ( value : value )
}
public func decode ( _ : Int . Type , forKey key : Key ) throws -> Int {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Int . self )
return try require ( value : value )
}
public func decode ( _ : Int8 . Type , forKey key : Key ) throws -> Int8 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Int8 . self )
return try require ( value : value )
}
public func decode ( _ : Int16 . Type , forKey key : Key ) throws -> Int16 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Int16 . self )
return try require ( value : value )
}
public func decode ( _ : Int32 . Type , forKey key : Key ) throws -> Int32 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Int32 . self )
return try require ( value : value )
}
public func decode ( _ : Int64 . Type , forKey key : Key ) throws -> Int64 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Int64 . self )
return try require ( value : value )
}
public func decode ( _ : UInt . Type , forKey key : Key ) throws -> UInt {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : UInt . self )
return try require ( value : value )
}
public func decode ( _ : UInt8 . Type , forKey key : Key ) throws -> UInt8 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : UInt8 . self )
return try require ( value : value )
}
public func decode ( _ : UInt16 . Type , forKey key : Key ) throws -> UInt16 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : UInt16 . self )
return try require ( value : value )
}
public func decode ( _ : UInt32 . Type , forKey key : Key ) throws -> UInt32 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : UInt32 . self )
return try require ( value : value )
}
public func decode ( _ : UInt64 . Type , forKey key : Key ) throws -> UInt64 {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : UInt64 . self )
return try require ( value : value )
}
public func decode ( _ : Float . Type , forKey key : Key ) throws -> Float {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Float . self )
return try require ( value : value )
}
public func decode ( _ : Double . Type , forKey key : Key ) throws -> Double {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : Double . self )
return try require ( value : value )
}
public func decode ( _ : String . Type , forKey key : Key ) throws -> String {
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : String . self )
return try require ( value : value )
}
2019-10-15 12:59:36 -07:00
public func decode < T : Decodable >( _ type : T . Type , forKey key : Key ) throws -> T {
2019-10-16 11:41:50 -07:00
#if compiler ( >= 5.1 )
if let type = type as ? DocumentIDProtocol . Type {
let docRef = decoder . userInfo [
Firestore . Decoder . documentRefUserInfoKey !
] as ! DocumentReference ?
2019-09-06 09:26:59 -04:00
2019-10-16 11:41:50 -07:00
if contains ( key ) {
let docPath = ( docRef != nil ) ? docRef !. path : "nil"
var codingPathCopy = codingPath . map { key in key . stringValue }
codingPathCopy . append ( key . stringValue )
2019-09-06 09:26:59 -04:00
2019-10-16 11:41:50 -07:00
throw FirestoreDecodingError . fieldNameConfict ( "Field name " +
" \( codingPathCopy ) was found from document \" \( docPath ) \" , " +
"cannot assign the document reference to this field." )
}
return try type . init ( from : docRef ) as ! T
2019-09-06 09:26:59 -04:00
}
2019-10-16 11:41:50 -07:00
#endif // compiler(>=5.1)
2019-09-06 09:26:59 -04:00
2019-06-19 10:00:17 -04:00
let entry = try require ( key : key )
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
let value = try decoder . unbox ( entry , as : T . self )
return try require ( value : value )
}
private func require ( key : Key ) throws -> Any {
if let entry = self . container [ key . stringValue ] {
return entry
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let description = "No value associated with key \( key ) ( \" \( key . stringValue ) \" )."
let context = DecodingError . Context ( codingPath : decoder . codingPath , debugDescription : description )
throw DecodingError . keyNotFound ( key , context )
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
private func require < T >( value : T ?) throws -> T {
if let value = value {
return value
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let message = "Expected \( T . self ) value but found null instead."
let context = DecodingError . Context ( codingPath : decoder . codingPath , debugDescription : message )
throw DecodingError . valueNotFound ( T . self , context )
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func nestedContainer < NestedKey >( keyedBy _ : NestedKey . Type , forKey key : Key ) throws -> KeyedDecodingContainer < NestedKey > {
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
guard let value = self . container [ key . stringValue ] else {
throw DecodingError . valueNotFound ( KeyedDecodingContainer < NestedKey >. self ,
DecodingError . Context ( codingPath : codingPath ,
debugDescription : "Cannot get nested keyed container -- no value found for key \" \( key . stringValue ) \" " ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
guard let dictionary = value as ? [ String : Any ] else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : [ String : Any ]. self , reality : value )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let container = _FirestoreKeyedDecodingContainer < NestedKey >( referencing : decoder , wrapping : dictionary )
return KeyedDecodingContainer ( container )
}
public func nestedUnkeyedContainer ( forKey key : Key ) throws -> UnkeyedDecodingContainer {
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
guard let value = self . container [ key . stringValue ] else {
throw DecodingError . valueNotFound ( UnkeyedDecodingContainer . self ,
DecodingError . Context ( codingPath : codingPath ,
debugDescription : "Cannot get nested unkeyed container -- no value found for key \" \( key . stringValue ) \" " ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
guard let array = value as ? [ Any ] else {
let context = DecodingError . Context ( codingPath : codingPath , debugDescription : "Not an array" )
throw DecodingError . typeMismatch ([ Any ]. self , context )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
return _FirestoreUnkeyedDecodingContainer ( referencing : decoder , wrapping : array )
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
private func _superDecoder ( forKey key : CodingKey ) throws -> Decoder {
decoder . codingPath . append ( key )
defer { self . decoder . codingPath . removeLast () }
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
let value : Any = container [ key . stringValue ] ?? NSNull ()
return _FirestoreDecoder ( referencing : value , at : decoder . codingPath )
}
public func superDecoder () throws -> Decoder {
return try _superDecoder ( forKey : _FirestoreKey . super )
}
public func superDecoder ( forKey key : Key ) throws -> Decoder {
return try _superDecoder ( forKey : key )
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
private struct _FirestoreUnkeyedDecodingContainer : UnkeyedDecodingContainer {
// MARK: Properties
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
/// A reference to the decoder we're reading from.
private let decoder : _FirestoreDecoder
/// A reference to the container we're reading from.
private let container : [ Any ]
/// The path of coding keys taken to get to this point in decoding.
public private ( set ) var codingPath : [ CodingKey ]
/// The index of the element we're about to decode.
public private ( set ) var currentIndex : Int
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
fileprivate init ( referencing decoder : _FirestoreDecoder , wrapping container : [ Any ]) {
self . decoder = decoder
self . container = container
codingPath = decoder . codingPath
currentIndex = 0
}
// MARK: - UnkeyedDecodingContainer Methods
public var count : Int ? {
return container . count
}
public var isAtEnd : Bool {
return currentIndex >= count !
}
public mutating func decodeNil () throws -> Bool {
try expectNotAtEnd ()
if container [ currentIndex ] is NSNull {
currentIndex += 1
return true
} else {
return false
}
}
public mutating func decode ( _ : Bool . Type ) throws -> Bool {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Bool . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Int . Type ) throws -> Int {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Int . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Int8 . Type ) throws -> Int8 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Int8 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Int16 . Type ) throws -> Int16 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Int16 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Int32 . Type ) throws -> Int32 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Int32 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Int64 . Type ) throws -> Int64 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Int64 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : UInt . Type ) throws -> UInt {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : UInt . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : UInt8 . Type ) throws -> UInt8 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : UInt8 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : UInt16 . Type ) throws -> UInt16 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : UInt16 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : UInt32 . Type ) throws -> UInt32 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : UInt32 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : UInt64 . Type ) throws -> UInt64 {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : UInt64 . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Float . Type ) throws -> Float {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Float . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : Double . Type ) throws -> Double {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : Double . self )
return try require ( value : decoded )
}
public mutating func decode ( _ : String . Type ) throws -> String {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : String . self )
return try require ( value : decoded )
}
public mutating func decode < T : Decodable >( _ : T . Type ) throws -> T {
try expectNotAtEnd ()
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
let decoded = try decoder . unbox ( container [ currentIndex ], as : T . self )
return try require ( value : decoded )
}
public mutating func nestedContainer < NestedKey >( keyedBy _ : NestedKey . Type ) throws -> KeyedDecodingContainer < NestedKey > {
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
try expectNotAtEnd ()
let value = self . container [ self . currentIndex ]
try requireNotNSNull ( value )
guard let dictionary = value as ? [ String : Any ] else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : [ String : Any ]. self , reality : value )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
currentIndex += 1
let container = _FirestoreKeyedDecodingContainer < NestedKey >( referencing : decoder , wrapping : dictionary )
return KeyedDecodingContainer ( container )
}
public mutating func nestedUnkeyedContainer () throws -> UnkeyedDecodingContainer {
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
try expectNotAtEnd ()
let value = container [ self . currentIndex ]
try requireNotNSNull ( value )
guard let array = value as ? [ Any ] else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : [ Any ]. self , reality : value )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
currentIndex += 1
return _FirestoreUnkeyedDecodingContainer ( referencing : decoder , wrapping : array )
}
public mutating func superDecoder () throws -> Decoder {
decoder . codingPath . append ( _FirestoreKey ( index : currentIndex ))
defer { self . decoder . codingPath . removeLast () }
try expectNotAtEnd ()
let value = container [ self . currentIndex ]
currentIndex += 1
return _FirestoreDecoder ( referencing : value , at : decoder . codingPath )
}
private func expectNotAtEnd () throws {
guard ! isAtEnd else {
throw DecodingError . valueNotFound ( Any ?. self , DecodingError . Context ( codingPath : decoder . codingPath + [ _FirestoreKey ( index : self . currentIndex )], debugDescription : "Unkeyed container is at end." ))
}
}
private func requireNotNSNull ( _ value : Any ) throws {
if ! ( value is NSNull ) {
return
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let description = "Cannot get keyed decoding container -- found null value instead."
let context = DecodingError . Context ( codingPath : codingPath , debugDescription : description )
throw DecodingError . valueNotFound ( UnkeyedDecodingContainer . self , context )
}
private mutating func require < T >( value : T ?) throws -> T {
guard let value = value else {
let message = "Expected \( T . self ) value but found null instead."
let context = DecodingError . Context ( codingPath : decoder . codingPath + [ _FirestoreKey ( index : currentIndex )], debugDescription : message )
throw DecodingError . valueNotFound ( T . self , context )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
currentIndex += 1
return value
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
extension _FirestoreDecoder : SingleValueDecodingContainer {
// MARK: SingleValueDecodingContainer Methods
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
private func expectNonNull < T >( _ type : T . Type ) throws {
guard ! decodeNil () else {
throw DecodingError . valueNotFound ( type , DecodingError . Context ( codingPath : codingPath , debugDescription : "Expected \( type ) but found null value instead." ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decodeNil () -> Bool {
return storage . topContainer is NSNull
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Bool . Type ) throws -> Bool {
try expectNonNull ( Bool . self )
return try unbox ( storage . topContainer , as : Bool . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Int . Type ) throws -> Int {
try expectNonNull ( Int . self )
return try unbox ( storage . topContainer , as : Int . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Int8 . Type ) throws -> Int8 {
try expectNonNull ( Int8 . self )
return try unbox ( storage . topContainer , as : Int8 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Int16 . Type ) throws -> Int16 {
try expectNonNull ( Int16 . self )
return try unbox ( storage . topContainer , as : Int16 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Int32 . Type ) throws -> Int32 {
try expectNonNull ( Int32 . self )
return try unbox ( storage . topContainer , as : Int32 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Int64 . Type ) throws -> Int64 {
try expectNonNull ( Int64 . self )
return try unbox ( storage . topContainer , as : Int64 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : UInt . Type ) throws -> UInt {
try expectNonNull ( UInt . self )
return try unbox ( storage . topContainer , as : UInt . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : UInt8 . Type ) throws -> UInt8 {
try expectNonNull ( UInt8 . self )
return try unbox ( storage . topContainer , as : UInt8 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : UInt16 . Type ) throws -> UInt16 {
try expectNonNull ( UInt16 . self )
return try unbox ( storage . topContainer , as : UInt16 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : UInt32 . Type ) throws -> UInt32 {
try expectNonNull ( UInt32 . self )
return try unbox ( storage . topContainer , as : UInt32 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : UInt64 . Type ) throws -> UInt64 {
try expectNonNull ( UInt64 . self )
return try unbox ( storage . topContainer , as : UInt64 . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Float . Type ) throws -> Float {
try expectNonNull ( Float . self )
return try unbox ( storage . topContainer , as : Float . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : Double . Type ) throws -> Double {
try expectNonNull ( Double . self )
return try unbox ( storage . topContainer , as : Double . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode ( _ : String . Type ) throws -> String {
try expectNonNull ( String . self )
return try unbox ( storage . topContainer , as : String . self ) !
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
public func decode < T : Decodable >( _ : T . Type ) throws -> T {
try expectNonNull ( T . self )
return try unbox ( storage . topContainer , as : T . self ) !
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
extension _FirestoreDecoder {
/// Returns the given value unboxed from a container.
func unbox ( _ value : Any , as type : Bool . Type ) throws -> Bool ? {
guard ! ( value is NSNull ) else { return nil }
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
if let number = value as ? NSNumber {
// TODO: Add a flag to coerce non-boolean numbers into Bools?
if number === kCFBooleanTrue as NSNumber {
return true
} else if number === kCFBooleanFalse as NSNumber {
return false
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
func unbox ( _ value : Any , as type : Int . Type ) throws -> Int ? {
guard ! ( value is NSNull ) else { return nil }
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let int = number . intValue
guard NSNumber ( value : int ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
return int
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
func unbox ( _ value : Any , as type : Int8 . Type ) throws -> Int8 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
let int8 = number . int8Value
guard NSNumber ( value : int8 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
return int8
}
func unbox ( _ value : Any , as type : Int16 . Type ) throws -> Int16 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let int16 = number . int16Value
guard NSNumber ( value : int16 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return int16
}
func unbox ( _ value : Any , as type : Int32 . Type ) throws -> Int32 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let int32 = number . int32Value
guard NSNumber ( value : int32 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return int32
}
func unbox ( _ value : Any , as type : Int64 . Type ) throws -> Int64 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let int64 = number . int64Value
guard NSNumber ( value : int64 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return int64
}
func unbox ( _ value : Any , as type : UInt . Type ) throws -> UInt ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let uint = number . uintValue
guard NSNumber ( value : uint ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return uint
}
func unbox ( _ value : Any , as type : UInt8 . Type ) throws -> UInt8 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let uint8 = number . uint8Value
guard NSNumber ( value : uint8 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return uint8
}
func unbox ( _ value : Any , as type : UInt16 . Type ) throws -> UInt16 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let uint16 = number . uint16Value
guard NSNumber ( value : uint16 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return uint16
}
func unbox ( _ value : Any , as type : UInt32 . Type ) throws -> UInt32 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let uint32 = number . uint32Value
guard NSNumber ( value : uint32 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return uint32
}
func unbox ( _ value : Any , as type : UInt64 . Type ) throws -> UInt64 ? {
guard ! ( value is NSNull ) else { return nil }
guard let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
let uint64 = number . uint64Value
guard NSNumber ( value : uint64 ) == number else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number < \( number ) > does not fit in \( type ) ." ))
}
return uint64
}
func unbox ( _ value : Any , as type : Float . Type ) throws -> Float ? {
guard ! ( value is NSNull ) else { return nil }
if let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse {
// We are willing to return a Float by losing precision:
// * If the original value was integral,
// * and the integral value was > Float.greatestFiniteMagnitude, we will fail
// * and the integral value was <= Float.greatestFiniteMagnitude, we are willing to lose precision past 2^24
// * If it was a Float, you will get back the precise value
// * If it was a Double or Decimal, you will get back the nearest approximation if it will fit
let double = number . doubleValue
guard abs ( double ) <= Double ( Float . greatestFiniteMagnitude ) else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath , debugDescription : "Decoded number \( number ) does not fit in \( type ) ." ))
}
return Float ( double )
}
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
func unbox ( _ value : Any , as type : Double . Type ) throws -> Double ? {
guard ! ( value is NSNull ) else { return nil }
if let number = value as ? NSNumber , number !== kCFBooleanTrue , number !== kCFBooleanFalse {
// We are always willing to return the number as a Double:
// * If the original value was integral, it is guaranteed to fit in a Double; we are willing to lose precision past 2^53 if you encoded a UInt64 but requested a Double
// * If it was a Float or Double, you will get back the precise value
// * If it was Decimal, you will get back the nearest approximation
return number . doubleValue
}
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
func unbox ( _ value : Any , as type : String . Type ) throws -> String ? {
guard ! ( value is NSNull ) else { return nil }
guard let string = value as ? String else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
return string
}
func unbox ( _ value : Any , as type : Date . Type ) throws -> Date ? {
guard ! ( value is NSNull ) else { return nil }
2019-09-06 09:26:59 -04:00
// Firestore returns all dates as Timestamp, converting it to Date so it can be used in custom objects.
if let timestamp = value as ? Timestamp {
return timestamp . dateValue ()
}
2019-06-19 10:00:17 -04:00
guard let date = value as ? Date else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
return date
}
func unbox ( _ value : Any , as type : Data . Type ) throws -> Data ? {
guard ! ( value is NSNull ) else { return nil }
guard let data = value as ? Data else {
throw DecodingError . _typeMismatch ( at : codingPath , expectation : type , reality : value )
}
return data
}
func unbox ( _ value : Any , as _ : Decimal . Type ) throws -> Decimal ? {
guard ! ( value is NSNull ) else { return nil }
// Attempt to bridge from NSDecimalNumber.
if let decimal = value as ? Decimal {
return decimal
} else {
let doubleValue = try unbox ( value , as : Double . self ) !
return Decimal ( doubleValue )
}
}
func unbox < T : Decodable >( _ value : Any , as _ : T . Type ) throws -> T ? {
if T . self == Date . self || T . self == NSDate . self {
guard let date = try self . unbox ( value , as : Date . self ) else { return nil }
return ( date as ! T )
}
if T . self == Data . self || T . self == NSData . self {
guard let data = try self . unbox ( value , as : Data . self ) else { return nil }
return ( data as ! T )
}
if T . self == URL . self || T . self == NSURL . self {
guard let urlString = try self . unbox ( value , as : String . self ) else {
return nil
}
guard let url = URL ( string : urlString ) else {
throw DecodingError . dataCorrupted ( DecodingError . Context ( codingPath : codingPath ,
debugDescription : "Invalid URL string." ))
}
return ( url as ! T )
}
if T . self == Decimal . self || T . self == NSDecimalNumber . self {
guard let decimal = try self . unbox ( value , as : Decimal . self ) else { return nil }
return ( decimal as ! T )
}
if let v = value as ? T {
if isFirestorePassthroughType ( v ) {
// All the native Firestore types that should not be encoded
return ( value as ! T )
}
}
2019-06-24 16:18:30 -04:00
// Decoding an embedded container, this requires expanding the storage stack and
2019-06-19 10:00:17 -04:00
// then restore after decoding.
storage . push ( container : value )
let decoded = try T ( from : self )
storage . popContainer ()
return decoded
}
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
extension DecodingError {
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 ))
}
2019-09-26 11:03:05 -07:00
2019-06-19 10:00:17 -04:00
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 )) "
2019-09-26 11:03:05 -07:00
}
2019-06-19 10:00:17 -04:00
}
2019-09-26 11:03:05 -07:00
}