// // Decoder.swift // CodableFirebase // // Created by Oleksii on 27/12/2017. // Copyright © 2017 ViolentOctopus. All rights reserved. // import Foundation class _FirebaseDecoder : Decoder { /// The strategy to use for decoding `Date` values. enum DateDecodingStrategy { /// Defer to `Date` for decoding. This is the default strategy. case deferredToDate /// Decode the `Date` as a UNIX timestamp from a JSON number. case secondsSince1970 /// Decode the `Date` as UNIX millisecond timestamp from a JSON number. case millisecondsSince1970 /// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). @available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) case iso8601 /// Decode the `Date` as a string parsed by the given formatter. case formatted(DateFormatter) /// Decode the `Date` as a custom value decoded by the given closure. case custom((_ decoder: Decoder) throws -> Date) } /// The strategy to use for decoding `Data` values. enum DataDecodingStrategy { /// Defer to `Data` for decoding. case deferredToData /// Decode the `Data` from a Base64-encoded string. This is the default strategy. case base64 /// Decode the `Data` as a custom value decoded by the given closure. case custom((_ decoder: Decoder) throws -> Data) } /// Options set on the top-level encoder to pass down the decoding hierarchy. struct _Options { let dateDecodingStrategy: DateDecodingStrategy? let dataDecodingStrategy: DataDecodingStrategy? let userInfo: [CodingUserInfoKey : Any] } // MARK: Properties /// The decoder's storage. fileprivate var storage: _FirebaseDecodingStorage fileprivate let options: _Options /// The path to the current point in encoding. fileprivate(set) public var codingPath: [CodingKey] /// Contextual user-provided information for use during encoding. public var userInfo: [CodingUserInfoKey : Any] { return options.userInfo } // MARK: - Initialization /// Initializes `self` with the given top-level container and options. init(referencing container: Any, at codingPath: [CodingKey] = [], options: _Options) { self.storage = _FirebaseDecodingStorage() self.storage.push(container: container) self.codingPath = codingPath self.options = options } // MARK: - Decoder Methods public func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer { guard !(self.storage.topContainer is NSNull) else { throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get keyed decoding container -- found null value instead.")) } 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) } let container = _FirebaseKeyedDecodingContainer(referencing: self, wrapping: topContainer) return KeyedDecodingContainer(container) } public func unkeyedContainer() throws -> UnkeyedDecodingContainer { guard !(self.storage.topContainer is NSNull) else { throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get unkeyed decoding container -- found null value instead.")) } 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) } return _FirebaseUnkeyedDecodingContainer(referencing: self, wrapping: topContainer) } public func singleValueContainer() throws -> SingleValueDecodingContainer { return self } } fileprivate struct _FirebaseDecodingStorage { // MARK: Properties /// The container stack. /// Elements may be any one of the plist types (NSNumber, Date, String, Array, [String : Any]). private(set) fileprivate var containers: [Any] = [] // MARK: - Initialization /// Initializes `self` with no containers. fileprivate init() {} // MARK: - Modifying the Stack fileprivate var count: Int { return containers.count } fileprivate var topContainer: Any { precondition(containers.count > 0, "Empty container stack.") return containers.last! } fileprivate mutating func push(container: Any) { containers.append(container) } fileprivate mutating func popContainer() { precondition(containers.count > 0, "Empty container stack.") containers.removeLast() } } fileprivate struct _FirebaseKeyedDecodingContainer : KeyedDecodingContainerProtocol { typealias Key = K // MARK: Properties /// A reference to the decoder we're reading from. private let decoder: _FirebaseDecoder /// A reference to the container we're reading from. private let container: [String : Any] /// The path of coding keys taken to get to this point in decoding. private(set) public var codingPath: [CodingKey] // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. fileprivate init(referencing decoder: _FirebaseDecoder, wrapping container: [String : Any]) { self.decoder = decoder self.container = container self.codingPath = decoder.codingPath } // MARK: - KeyedDecodingContainerProtocol Methods public var allKeys: [Key] { return container.keys.flatMap { Key(stringValue: $0) } } public func contains(_ key: Key) -> Bool { return container[key.stringValue] != nil } public func decodeNil(forKey key: Key) throws -> Bool { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } return entry is NSNull } public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Bool.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Int.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Int8.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Int16.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Int32.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Int64.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: UInt.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { guard let entry = container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: UInt8.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { guard let entry = container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: UInt16.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: UInt32.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: UInt64.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Float.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: Double.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: String.Type, forKey key: Key) throws -> String { guard let entry = self.container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try self.decoder.unbox(entry, as: String.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func decode(_ type: T.Type, forKey key: Key) throws -> T { guard let entry = container[key.stringValue] else { throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) } self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = try decoder.unbox(entry, as: T.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) } return value } public func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } guard let value = self.container[key.stringValue] else { throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get nested keyed container -- no value found for key \"\(key.stringValue)\"")) } guard let dictionary = value as? [String : Any] else { throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value) } let container = _FirebaseKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) return KeyedDecodingContainer(container) } public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { self.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: self.codingPath, debugDescription: "Cannot get nested unkeyed container -- no value found for key \"\(key.stringValue)\"")) } guard let array = value as? [Any] else { let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Not an array") throw DecodingError.typeMismatch([Any].self, context) } return _FirebaseUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) } private func _superDecoder(forKey key: CodingKey) throws -> Decoder { self.decoder.codingPath.append(key) defer { self.decoder.codingPath.removeLast() } let value: Any = container[key.stringValue] ?? NSNull() return _FirebaseDecoder(referencing: value, at: self.decoder.codingPath, options: decoder.options) } public func superDecoder() throws -> Decoder { return try _superDecoder(forKey: _FirebaseKey.super) } public func superDecoder(forKey key: Key) throws -> Decoder { return try _superDecoder(forKey: key) } } fileprivate struct _FirebaseUnkeyedDecodingContainer : UnkeyedDecodingContainer { // MARK: Properties /// A reference to the decoder we're reading from. private let decoder: _FirebaseDecoder /// 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. private(set) public var codingPath: [CodingKey] /// The index of the element we're about to decode. private(set) public var currentIndex: Int // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. fileprivate init(referencing decoder: _FirebaseDecoder, wrapping container: [Any]) { self.decoder = decoder self.container = container self.codingPath = decoder.codingPath self.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 { guard !isAtEnd else { throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) } if container[currentIndex] is NSNull { currentIndex += 1 return true } else { return false } } public mutating func decode(_ type: Bool.Type) throws -> Bool { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Bool.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Int.Type) throws -> Int { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Int.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Int8.Type) throws -> Int8 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Int8.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Int16.Type) throws -> Int16 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Int16.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Int32.Type) throws -> Int32 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Int32.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Int64.Type) throws -> Int64 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: Int64.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: UInt.Type) throws -> UInt { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: UInt.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: UInt8.Type) throws -> UInt8 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: UInt8.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: UInt16.Type) throws -> UInt16 { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } self.decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: UInt16.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: UInt32.Type) throws -> UInt32 { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: UInt32.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: UInt64.Type) throws -> UInt64 { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Unkeyed container is at end.")) } decoder.codingPath.append(_FirebaseKey(index: currentIndex)) defer { decoder.codingPath.removeLast() } guard let decoded = try decoder.unbox(container[currentIndex], as: UInt64.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: decoder.codingPath + [_FirebaseKey(index: currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } currentIndex += 1 return decoded } public mutating func decode(_ type: Float.Type) throws -> Float { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) } self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } self.currentIndex += 1 return decoded } public mutating func decode(_ type: Double.Type) throws -> Double { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) } self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } self.currentIndex += 1 return decoded } public mutating func decode(_ type: String.Type) throws -> String { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) } self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } self.currentIndex += 1 return decoded } public mutating func decode(_ type: T.Type) throws -> T { guard !self.isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) } self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: T.self) else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_FirebaseKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) } self.currentIndex += 1 return decoded } public mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer { self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !self.isAtEnd else { throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get nested keyed container -- unkeyed container is at end.")) } let value = self.container[self.currentIndex] guard !(value is NSNull) else { throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get keyed decoding container -- found null value instead.")) } guard let dictionary = value as? [String : Any] else { let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Not a dictionary") throw DecodingError.typeMismatch([String : Any].self, context) } self.currentIndex += 1 let container = _FirebaseKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) return KeyedDecodingContainer(container) } public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !self.isAtEnd else { throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get nested unkeyed container -- unkeyed container is at end.")) } let value = self.container[self.currentIndex] guard !(value is NSNull) else { throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get keyed decoding container -- found null value instead.")) } guard let array = value as? [Any] else { let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Not an array") throw DecodingError.typeMismatch([String : Any].self, context) } self.currentIndex += 1 return _FirebaseUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) } public mutating func superDecoder() throws -> Decoder { self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !self.isAtEnd else { throw DecodingError.valueNotFound(Decoder.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot get superDecoder() -- unkeyed container is at end.")) } let value = self.container[self.currentIndex] self.currentIndex += 1 return _FirebaseDecoder(referencing: value, at: decoder.codingPath, options: decoder.options) } } extension _FirebaseDecoder : SingleValueDecodingContainer { // MARK: SingleValueDecodingContainer Methods private func expectNonNull(_ type: T.Type) throws { guard !decodeNil() else { throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Expected \(type) but found null value instead.")) } } public func decodeNil() -> Bool { return storage.topContainer is NSNull } public func decode(_ type: Bool.Type) throws -> Bool { try expectNonNull(Bool.self) return try self.unbox(self.storage.topContainer, as: Bool.self)! } public func decode(_ type: Int.Type) throws -> Int { try expectNonNull(Int.self) return try self.unbox(self.storage.topContainer, as: Int.self)! } public func decode(_ type: Int8.Type) throws -> Int8 { try expectNonNull(Int8.self) return try self.unbox(self.storage.topContainer, as: Int8.self)! } public func decode(_ type: Int16.Type) throws -> Int16 { try expectNonNull(Int16.self) return try self.unbox(self.storage.topContainer, as: Int16.self)! } public func decode(_ type: Int32.Type) throws -> Int32 { try expectNonNull(Int32.self) return try self.unbox(self.storage.topContainer, as: Int32.self)! } public func decode(_ type: Int64.Type) throws -> Int64 { try expectNonNull(Int64.self) return try self.unbox(self.storage.topContainer, as: Int64.self)! } public func decode(_ type: UInt.Type) throws -> UInt { try expectNonNull(UInt.self) return try self.unbox(self.storage.topContainer, as: UInt.self)! } public func decode(_ type: UInt8.Type) throws -> UInt8 { try expectNonNull(UInt8.self) return try self.unbox(self.storage.topContainer, as: UInt8.self)! } public func decode(_ type: UInt16.Type) throws -> UInt16 { try expectNonNull(UInt16.self) return try self.unbox(self.storage.topContainer, as: UInt16.self)! } public func decode(_ type: UInt32.Type) throws -> UInt32 { try expectNonNull(UInt32.self) return try self.unbox(self.storage.topContainer, as: UInt32.self)! } public func decode(_ type: UInt64.Type) throws -> UInt64 { try expectNonNull(UInt64.self) return try self.unbox(self.storage.topContainer, as: UInt64.self)! } public func decode(_ type: Float.Type) throws -> Float { try expectNonNull(Float.self) return try self.unbox(self.storage.topContainer, as: Float.self)! } public func decode(_ type: Double.Type) throws -> Double { try expectNonNull(Double.self) return try self.unbox(self.storage.topContainer, as: Double.self)! } public func decode(_ type: String.Type) throws -> String { try expectNonNull(String.self) return try self.unbox(self.storage.topContainer, as: String.self)! } public func decode(_ type: T.Type) throws -> T { try expectNonNull(T.self) return try self.unbox(self.storage.topContainer, as: T.self)! } } extension _FirebaseDecoder { /// 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 } 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 } /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: } else if let bool = value as? Bool { return bool */ } throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) } func unbox(_ value: Any, as type: Int.Type) throws -> Int? { guard !(value is NSNull) else { return nil } guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) } let int = number.intValue guard NSNumber(value: int) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) } return int } 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: self.codingPath, expectation: type, reality: value) } let int8 = number.int8Value guard NSNumber(value: int8) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) } 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: self.codingPath, expectation: type, reality: value) } let int16 = number.int16Value guard NSNumber(value: int16) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let int32 = number.int32Value guard NSNumber(value: int32) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let int64 = number.int64Value guard NSNumber(value: int64) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let uint = number.uintValue guard NSNumber(value: uint) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let uint8 = number.uint8Value guard NSNumber(value: uint8) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let uint16 = number.uint16Value guard NSNumber(value: uint16) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let uint32 = number.uint32Value guard NSNumber(value: uint32) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, expectation: type, reality: value) } let uint64 = number.uint64Value guard NSNumber(value: uint64) == number else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON 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: self.codingPath, debugDescription: "Parsed JSON number \(number) does not fit in \(type).")) } return Float(double) /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: } else if let double = value as? Double { if abs(double) <= Double(Float.max) { return Float(double) } overflow = true } else if let int = value as? Int { if let float = Float(exactly: int) { return float } overflow = true */ } throw DecodingError._typeMismatch(at: self.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 /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: } else if let double = value as? Double { return double } else if let int = value as? Int { if let double = Double(exactly: int) { return double } overflow = true */ } throw DecodingError._typeMismatch(at: self.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: self.codingPath, expectation: type, reality: value) } return string } func unbox(_ value: Any, as type: Date.Type) throws -> Date? { guard !(value is NSNull) else { return nil } guard let options = options.dateDecodingStrategy else { guard let date = value as? Date else { throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value) } return date } switch options { case .deferredToDate: self.storage.push(container: value) let date = try Date(from: self) self.storage.popContainer() return date case .secondsSince1970: let double = try self.unbox(value, as: Double.self)! return Date(timeIntervalSince1970: double) case .millisecondsSince1970: let double = try self.unbox(value, as: Double.self)! return Date(timeIntervalSince1970: double / 1000.0) case .iso8601: if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { let string = try self.unbox(value, as: String.self)! guard let date = _iso8601Formatter.date(from: string) else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted.")) } return date } else { fatalError("ISO8601DateFormatter is unavailable on this platform.") } case .formatted(let formatter): let string = try self.unbox(value, as: String.self)! guard let date = formatter.date(from: string) else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Date string does not match format expected by formatter.")) } return date case .custom(let closure): self.storage.push(container: value) let date = try closure(self) self.storage.popContainer() 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 type: 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 self.unbox(value, as: Double.self)! return Decimal(doubleValue) } } func unbox(_ value: Any, as type: T.Type) throws -> T? { let decoded: T if T.self == Date.self || T.self == NSDate.self { guard let date = try self.unbox(value, as: Date.self) else { return nil } decoded = date as! T } else if T.self == Data.self || T.self == NSData.self { guard let data = try self.unbox(value, as: Data.self) else { return nil } decoded = data as! T } else 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: self.codingPath, debugDescription: "Invalid URL string.")) } decoded = (url as! T) } else if T.self == Decimal.self || T.self == NSDecimalNumber.self { guard let decimal = try self.unbox(value, as: Decimal.self) else { return nil } decoded = decimal as! T } else { self.storage.push(container: value) decoded = try T(from: self) self.storage.popContainer() } return decoded } } @available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) fileprivate var _iso8601Formatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = .withInternetDateTime return formatter }()