mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-04-03 17:05:28 +08:00
1255 lines
56 KiB
Swift
1255 lines
56 KiB
Swift
//
|
|
// 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<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
|
|
guard !(self.storage.topContainer is NSNull) else {
|
|
throw DecodingError.valueNotFound(KeyedDecodingContainer<Key>.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<Key>(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<K : CodingKey> : 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<T : Decodable>(_ 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<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
|
|
self.decoder.codingPath.append(key)
|
|
defer { self.decoder.codingPath.removeLast() }
|
|
|
|
guard let value = self.container[key.stringValue] else {
|
|
throw DecodingError.valueNotFound(KeyedDecodingContainer<NestedKey>.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<NestedKey>(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<T : Decodable>(_ 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<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
|
|
self.decoder.codingPath.append(_FirebaseKey(index: self.currentIndex))
|
|
defer { self.decoder.codingPath.removeLast() }
|
|
|
|
guard !self.isAtEnd else {
|
|
throw DecodingError.valueNotFound(KeyedDecodingContainer<NestedKey>.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<NestedKey>.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<NestedKey>(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<T>(_ 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<T : Decodable>(_ 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<T : Decodable>(_ 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
|
|
}()
|