create
This commit is contained in:
412
Pods/FirebaseSharedSwift/FirebaseSharedSwift/LICENSE
generated
Normal file
412
Pods/FirebaseSharedSwift/FirebaseSharedSwift/LICENSE
generated
Normal file
@@ -0,0 +1,412 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Runtime Library Exception to the Apache 2.0 License: ##
|
||||
|
||||
|
||||
As an exception, if you use this Software to compile your source code and
|
||||
portions of this Software are embedded into the binary product as a result,
|
||||
you may redistribute such product without providing attribution as would
|
||||
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
26
Pods/FirebaseSharedSwift/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift
generated
Normal file
26
Pods/FirebaseSharedSwift/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Conform to this protocol for the Firebase Decoder to extract values as from a RemoteConfigValue
|
||||
/// object.
|
||||
public protocol FirebaseRemoteConfigValueDecoding {
|
||||
func numberValue() -> NSNumber
|
||||
func boolValue() -> Bool
|
||||
func stringValue() -> String
|
||||
func dataValue() -> Data
|
||||
func arrayValue() -> [AnyHashable]?
|
||||
func dictionaryValue() -> [String: AnyHashable]?
|
||||
}
|
||||
2655
Pods/FirebaseSharedSwift/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift
generated
vendored
Normal file
2655
Pods/FirebaseSharedSwift/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift
generated
vendored
Normal file
@@ -0,0 +1,2655 @@
|
||||
// This file is derived from swift/stdlib/public/Darwin/Foundation/JSONEncoder.swift
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol StructureCodingPassthroughTypeResolver {
|
||||
static func isPassthroughType<T>(_ t: T) -> Bool
|
||||
}
|
||||
|
||||
private struct NoPassthroughTypes: StructureCodingPassthroughTypeResolver {
|
||||
static func isPassthroughType<T>(_ t: T) -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public protocol StructureCodingUncodedUnkeyed {}
|
||||
|
||||
extension DecodingError {
|
||||
/// Returns a `.typeMismatch` error describing the expected type.
|
||||
///
|
||||
/// - parameter path: The path of `CodingKey`s taken to decode a value of this type.
|
||||
/// - parameter expectation: The type expected to be encountered.
|
||||
/// - parameter reality: The value that was encountered instead of the expected type.
|
||||
/// - returns: A `DecodingError` with the appropriate path and debug description.
|
||||
internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError {
|
||||
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
|
||||
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
|
||||
}
|
||||
|
||||
/// Returns a description of the type of `value` appropriate for an error message.
|
||||
///
|
||||
/// - parameter value: The value whose type to describe.
|
||||
/// - returns: A string describing `value`.
|
||||
/// - precondition: `value` is one of the types below.
|
||||
fileprivate static func _typeDescription(of value: Any) -> String {
|
||||
if value is NSNull {
|
||||
return "a null value"
|
||||
} else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ {
|
||||
return "a number"
|
||||
} else if value is String {
|
||||
return "a string/data"
|
||||
} else if value is [Any] {
|
||||
return "an array"
|
||||
} else if value is [String : Any] {
|
||||
return "a dictionary"
|
||||
} else {
|
||||
return "\(type(of: value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A marker protocol used to determine whether a value is a `String`-keyed `Dictionary`
|
||||
/// containing `Encodable` values (in which case it should be exempt from key conversion strategies).
|
||||
///
|
||||
/// NOTE: The architecture and environment check is due to a bug in the current (2018-08-08) Swift 4.2
|
||||
/// runtime when running on i386 simulator. The issue is tracked in https://bugs.swift.org/browse/SR-8276
|
||||
/// Making the protocol `internal` instead of `fileprivate` works around this issue.
|
||||
/// Once SR-8276 is fixed, this check can be removed and the protocol always be made fileprivate.
|
||||
#if arch(i386) || arch(arm)
|
||||
internal protocol _JSONStringDictionaryEncodableMarker { }
|
||||
#else
|
||||
fileprivate protocol _JSONStringDictionaryEncodableMarker { }
|
||||
#endif
|
||||
|
||||
extension Dictionary : _JSONStringDictionaryEncodableMarker where Key == String, Value: Encodable { }
|
||||
|
||||
/// A marker protocol used to determine whether a value is a `String`-keyed `Dictionary`
|
||||
/// containing `Decodable` values (in which case it should be exempt from key conversion strategies).
|
||||
///
|
||||
/// The marker protocol also provides access to the type of the `Decodable` values,
|
||||
/// which is needed for the implementation of the key conversion strategy exemption.
|
||||
///
|
||||
/// NOTE: Please see comment above regarding SR-8276
|
||||
#if arch(i386) || arch(arm)
|
||||
internal protocol _JSONStringDictionaryDecodableMarker {
|
||||
static var elementType: Decodable.Type { get }
|
||||
}
|
||||
#else
|
||||
fileprivate protocol _JSONStringDictionaryDecodableMarker {
|
||||
static var elementType: Decodable.Type { get }
|
||||
}
|
||||
#endif
|
||||
|
||||
extension Dictionary : _JSONStringDictionaryDecodableMarker where Key == String, Value: Decodable {
|
||||
static var elementType: Decodable.Type { return Value.self }
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// JSON Encoder
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// `JSONEncoder` facilitates the encoding of `Encodable` values into JSON.
|
||||
// NOTE: older overlays had Foundation.JSONEncoder as the ObjC name.
|
||||
// The two must coexist, so it was renamed. The old name must not be
|
||||
// used in the new runtime. _TtC10Foundation13__JSONEncoder is the
|
||||
// mangled name for Foundation.__JSONEncoder.
|
||||
|
||||
public class FirebaseDataEncoder {
|
||||
// MARK: Options
|
||||
|
||||
/// The strategy to use for encoding `Date` values.
|
||||
public enum DateEncodingStrategy {
|
||||
/// Defer to `Date` for choosing an encoding. This is the default strategy.
|
||||
case deferredToDate
|
||||
|
||||
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
|
||||
case secondsSince1970
|
||||
|
||||
/// Encode the `Date` as UNIX millisecond timestamp (as a JSON number).
|
||||
case millisecondsSince1970
|
||||
|
||||
/// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
|
||||
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||
case iso8601
|
||||
|
||||
/// Encode the `Date` as a string formatted by the given formatter.
|
||||
case formatted(DateFormatter)
|
||||
|
||||
/// Encode the `Date` as a custom value encoded by the given closure.
|
||||
///
|
||||
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
|
||||
case custom((Date, Swift.Encoder) throws -> Void)
|
||||
}
|
||||
|
||||
/// The strategy to use for encoding `Data` values.
|
||||
public enum DataEncodingStrategy {
|
||||
/// Defer to `Data` for choosing an encoding.
|
||||
case deferredToData
|
||||
|
||||
/// Encode the `Data` as a Base64-encoded string. This is the default strategy.
|
||||
case base64
|
||||
|
||||
/// Encode the `Data` as an `NSData` blob.
|
||||
case blob
|
||||
|
||||
/// Encode the `Data` as a custom value encoded by the given closure.
|
||||
///
|
||||
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
|
||||
case custom((Data, Swift.Encoder) throws -> Void)
|
||||
}
|
||||
|
||||
/// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN).
|
||||
public enum NonConformingFloatEncodingStrategy {
|
||||
/// Throw upon encountering non-conforming values. This is the default strategy.
|
||||
case `throw`
|
||||
|
||||
/// Encode the values using the given representation strings.
|
||||
case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String)
|
||||
}
|
||||
|
||||
/// The strategy to use for automatically changing the value of keys before encoding.
|
||||
public enum KeyEncodingStrategy {
|
||||
/// Use the keys specified by each type. This is the default strategy.
|
||||
case useDefaultKeys
|
||||
|
||||
/// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key to JSON payload.
|
||||
///
|
||||
/// Capital characters are determined by testing membership in `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` (Unicode General Categories Lu and Lt).
|
||||
/// The conversion to lower case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences.
|
||||
///
|
||||
/// Converting from camel case to snake case:
|
||||
/// 1. Splits words at the boundary of lower-case to upper-case
|
||||
/// 2. Inserts `_` between words
|
||||
/// 3. Lowercases the entire string
|
||||
/// 4. Preserves starting and ending `_`.
|
||||
///
|
||||
/// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`.
|
||||
///
|
||||
/// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted.
|
||||
case convertToSnakeCase
|
||||
|
||||
/// Provide a custom conversion to the key in the encoded JSON from the keys specified by the encoded types.
|
||||
/// The full path to the current encoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before encoding.
|
||||
/// If the result of the conversion is a duplicate key, then only one value will be present in the result.
|
||||
case custom((_ codingPath: [CodingKey]) -> CodingKey)
|
||||
|
||||
fileprivate static func _convertToSnakeCase(_ stringKey: String) -> String {
|
||||
guard !stringKey.isEmpty else { return stringKey }
|
||||
|
||||
var words : [Range<String.Index>] = []
|
||||
// The general idea of this algorithm is to split words on transition from lower to upper case, then on transition of >1 upper case characters to lowercase
|
||||
//
|
||||
// myProperty -> my_property
|
||||
// myURLProperty -> my_url_property
|
||||
//
|
||||
// We assume, per Swift naming conventions, that the first character of the key is lowercase.
|
||||
var wordStart = stringKey.startIndex
|
||||
var searchRange = stringKey.index(after: wordStart)..<stringKey.endIndex
|
||||
|
||||
// Find next uppercase character
|
||||
while let upperCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.uppercaseLetters, options: [], range: searchRange) {
|
||||
let untilUpperCase = wordStart..<upperCaseRange.lowerBound
|
||||
words.append(untilUpperCase)
|
||||
|
||||
// Find next lowercase character
|
||||
searchRange = upperCaseRange.lowerBound..<searchRange.upperBound
|
||||
guard let lowerCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.lowercaseLetters, options: [], range: searchRange) else {
|
||||
// There are no more lower case letters. Just end here.
|
||||
wordStart = searchRange.lowerBound
|
||||
break
|
||||
}
|
||||
|
||||
// Is the next lowercase letter more than 1 after the uppercase? If so, we encountered a group of uppercase letters that we should treat as its own word
|
||||
let nextCharacterAfterCapital = stringKey.index(after: upperCaseRange.lowerBound)
|
||||
if lowerCaseRange.lowerBound == nextCharacterAfterCapital {
|
||||
// The next character after capital is a lower case character and therefore not a word boundary.
|
||||
// Continue searching for the next upper case for the boundary.
|
||||
wordStart = upperCaseRange.lowerBound
|
||||
} else {
|
||||
// There was a range of >1 capital letters. Turn those into a word, stopping at the capital before the lower case character.
|
||||
let beforeLowerIndex = stringKey.index(before: lowerCaseRange.lowerBound)
|
||||
words.append(upperCaseRange.lowerBound..<beforeLowerIndex)
|
||||
|
||||
// Next word starts at the capital before the lowercase we just found
|
||||
wordStart = beforeLowerIndex
|
||||
}
|
||||
searchRange = lowerCaseRange.upperBound..<searchRange.upperBound
|
||||
}
|
||||
words.append(wordStart..<searchRange.upperBound)
|
||||
let result = words.map({ (range) in
|
||||
return stringKey[range].lowercased()
|
||||
}).joined(separator: "_")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/// The strategy to use in encoding dates. Defaults to `.deferredToDate`.
|
||||
open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate
|
||||
|
||||
/// The strategy to use in encoding binary data. Defaults to `.base64`.
|
||||
open var dataEncodingStrategy: DataEncodingStrategy = .base64
|
||||
|
||||
/// The strategy to use in encoding non-conforming numbers. Defaults to `.throw`.
|
||||
open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy = .throw
|
||||
|
||||
/// The strategy to use for encoding keys. Defaults to `.useDefaultKeys`.
|
||||
open var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys
|
||||
|
||||
/// A type that can resolve which types to 'pass through' - or leave alone while encoding. Defaults to not passing any types through.
|
||||
open var passthroughTypeResolver: StructureCodingPassthroughTypeResolver.Type = NoPassthroughTypes.self
|
||||
|
||||
/// Contextual user-provided information for use during encoding.
|
||||
open var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
/// Options set on the top-level encoder to pass down the encoding hierarchy.
|
||||
fileprivate struct _Options {
|
||||
let dateEncodingStrategy: DateEncodingStrategy
|
||||
let dataEncodingStrategy: DataEncodingStrategy
|
||||
let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy
|
||||
let keyEncodingStrategy: KeyEncodingStrategy
|
||||
let passthroughTypeResolver: StructureCodingPassthroughTypeResolver.Type
|
||||
let userInfo: [CodingUserInfoKey : Any]
|
||||
}
|
||||
|
||||
/// The options set on the top-level encoder.
|
||||
fileprivate var options: _Options {
|
||||
return _Options(dateEncodingStrategy: dateEncodingStrategy,
|
||||
dataEncodingStrategy: dataEncodingStrategy,
|
||||
nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy,
|
||||
keyEncodingStrategy: keyEncodingStrategy,
|
||||
passthroughTypeResolver: passthroughTypeResolver,
|
||||
userInfo: userInfo)
|
||||
}
|
||||
|
||||
// MARK: - Constructing a JSON Encoder
|
||||
|
||||
/// Initializes `self` with default strategies.
|
||||
public init() {}
|
||||
|
||||
// MARK: - Encoding Values
|
||||
|
||||
/// Encodes the given top-level value and returns its JSON representation.
|
||||
///
|
||||
/// - parameter value: The value to encode.
|
||||
/// - returns: A new `Data` value containing the encoded JSON data.
|
||||
/// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`.
|
||||
/// - throws: An error if any value throws an error during encoding.
|
||||
open func encode<T : Encodable>(_ value: T) throws -> Any {
|
||||
let encoder = __JSONEncoder(options: self.options)
|
||||
|
||||
guard let topLevel = try encoder.box_(value) else {
|
||||
throw Swift.EncodingError.invalidValue(value,
|
||||
Swift.EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) did not encode any values."))
|
||||
}
|
||||
return topLevel
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - __JSONEncoder
|
||||
|
||||
// NOTE: older overlays called this class _JSONEncoder.
|
||||
// The two must coexist without a conflicting ObjC class name, so it
|
||||
// was renamed. The old name must not be used in the new runtime.
|
||||
fileprivate class __JSONEncoder : Encoder {
|
||||
// MARK: Properties
|
||||
|
||||
/// The encoder's storage.
|
||||
fileprivate var storage: _JSONEncodingStorage
|
||||
|
||||
/// Options set on the top-level encoder.
|
||||
fileprivate let options: FirebaseDataEncoder._Options
|
||||
|
||||
/// The path to the current point in encoding.
|
||||
public var codingPath: [CodingKey]
|
||||
|
||||
/// Contextual user-provided information for use during encoding.
|
||||
public var userInfo: [CodingUserInfoKey : Any] {
|
||||
return self.options.userInfo
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` with the given top-level encoder options.
|
||||
fileprivate init(options: FirebaseDataEncoder._Options, codingPath: [CodingKey] = []) {
|
||||
self.options = options
|
||||
self.storage = _JSONEncodingStorage()
|
||||
self.codingPath = codingPath
|
||||
}
|
||||
|
||||
/// Returns whether a new element can be encoded at this coding path.
|
||||
///
|
||||
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
|
||||
fileprivate var canEncodeNewValue: Bool {
|
||||
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
|
||||
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
|
||||
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
|
||||
//
|
||||
// This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path.
|
||||
// Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here).
|
||||
return self.storage.count == self.codingPath.count
|
||||
}
|
||||
|
||||
// MARK: - Encoder Methods
|
||||
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
|
||||
// If an existing keyed container was already requested, return that one.
|
||||
let topContainer: NSMutableDictionary
|
||||
if self.canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = self.storage.pushKeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableDictionary else {
|
||||
preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
let container = _JSONKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public func unkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
// If an existing unkeyed container was already requested, return that one.
|
||||
let topContainer: NSMutableArray
|
||||
if self.canEncodeNewValue {
|
||||
// We haven't yet pushed a container at this level; do so here.
|
||||
topContainer = self.storage.pushUnkeyedContainer()
|
||||
} else {
|
||||
guard let container = self.storage.containers.last as? NSMutableArray else {
|
||||
preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
|
||||
}
|
||||
|
||||
topContainer = container
|
||||
}
|
||||
|
||||
return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
|
||||
}
|
||||
|
||||
public func singleValueContainer() -> SingleValueEncodingContainer {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Encoding Storage and Containers
|
||||
|
||||
fileprivate struct _JSONEncodingStorage {
|
||||
// MARK: Properties
|
||||
|
||||
/// The container stack.
|
||||
/// Elements may be any one of the JSON types (NSNull, NSNumber, NSString, NSArray, NSDictionary).
|
||||
private(set) fileprivate var containers: [NSObject] = []
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` with no containers.
|
||||
fileprivate init() {}
|
||||
|
||||
// MARK: - Modifying the Stack
|
||||
|
||||
fileprivate var count: Int {
|
||||
return self.containers.count
|
||||
}
|
||||
|
||||
fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.containers.append(dictionary)
|
||||
return dictionary
|
||||
}
|
||||
|
||||
fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
|
||||
let array = NSMutableArray()
|
||||
self.containers.append(array)
|
||||
return array
|
||||
}
|
||||
|
||||
fileprivate mutating func push(container: __owned NSObject) {
|
||||
self.containers.append(container)
|
||||
}
|
||||
|
||||
fileprivate mutating func popContainer() -> NSObject {
|
||||
precondition(!self.containers.isEmpty, "Empty container stack.")
|
||||
return self.containers.popLast()!
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Encoding Containers
|
||||
|
||||
fileprivate struct _JSONKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
|
||||
typealias Key = K
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: __JSONEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableDictionary
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: __JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - Coding Path Operations
|
||||
|
||||
private func _converted(_ key: CodingKey) -> CodingKey {
|
||||
switch encoder.options.keyEncodingStrategy {
|
||||
case .useDefaultKeys:
|
||||
return key
|
||||
case .convertToSnakeCase:
|
||||
let newKeyString = FirebaseDataEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue)
|
||||
return _JSONKey(stringValue: newKeyString, intValue: key.intValue)
|
||||
case .custom(let converter):
|
||||
return converter(codingPath + [key])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||
|
||||
public mutating func encodeNil(forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = NSNull()
|
||||
}
|
||||
public mutating func encode(_ value: Bool, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: Int, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: Int8, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: Int16, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: Int32, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: Int64, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: UInt, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: UInt8, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: UInt16, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: UInt32, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: UInt64, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
public mutating func encode(_ value: String, forKey key: Key) throws {
|
||||
self.container[_converted(key).stringValue] = self.encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func encode(_ value: Float, forKey key: Key) throws {
|
||||
// Since the float may be invalid and throw, the coding path needs to contain this key.
|
||||
self.encoder.codingPath.append(key)
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container[_converted(key).stringValue] = try self.encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func encode(_ value: Double, forKey key: Key) throws {
|
||||
// Since the double may be invalid and throw, the coding path needs to contain this key.
|
||||
self.encoder.codingPath.append(key)
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container[_converted(key).stringValue] = try self.encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||
if T.self is StructureCodingUncodedUnkeyed.Type { return }
|
||||
self.encoder.codingPath.append(key)
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container[_converted(key).stringValue] = try self.encoder.box(value)
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
|
||||
let containerKey = _converted(key).stringValue
|
||||
let dictionary: NSMutableDictionary
|
||||
if let existingContainer = self.container[containerKey] {
|
||||
precondition(
|
||||
existingContainer is NSMutableDictionary,
|
||||
"Attempt to re-encode into nested KeyedEncodingContainer<\(Key.self)> for key \"\(containerKey)\" is invalid: non-keyed container already encoded for this key"
|
||||
)
|
||||
dictionary = existingContainer as! NSMutableDictionary
|
||||
} else {
|
||||
dictionary = NSMutableDictionary()
|
||||
self.container[containerKey] = dictionary
|
||||
}
|
||||
|
||||
self.codingPath.append(key)
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let container = _JSONKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
||||
let containerKey = _converted(key).stringValue
|
||||
let array: NSMutableArray
|
||||
if let existingContainer = self.container[containerKey] {
|
||||
precondition(
|
||||
existingContainer is NSMutableArray,
|
||||
"Attempt to re-encode into nested UnkeyedEncodingContainer for key \"\(containerKey)\" is invalid: keyed container/single value already encoded for this key"
|
||||
)
|
||||
array = existingContainer as! NSMutableArray
|
||||
} else {
|
||||
array = NSMutableArray()
|
||||
self.container[containerKey] = array
|
||||
}
|
||||
|
||||
self.codingPath.append(key)
|
||||
defer { self.codingPath.removeLast() }
|
||||
return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return __JSONReferencingEncoder(referencing: self.encoder, key: _JSONKey.super, convertedKey: _converted(_JSONKey.super), wrapping: self.container)
|
||||
}
|
||||
|
||||
public mutating func superEncoder(forKey key: Key) -> Encoder {
|
||||
return __JSONReferencingEncoder(referencing: self.encoder, key: key, convertedKey: _converted(key), wrapping: self.container)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _JSONUnkeyedEncodingContainer : UnkeyedEncodingContainer {
|
||||
// MARK: Properties
|
||||
|
||||
/// A reference to the encoder we're writing to.
|
||||
private let encoder: __JSONEncoder
|
||||
|
||||
/// A reference to the container we're writing to.
|
||||
private let container: NSMutableArray
|
||||
|
||||
/// The path of coding keys taken to get to this point in encoding.
|
||||
private(set) public var codingPath: [CodingKey]
|
||||
|
||||
/// The number of elements encoded into the container.
|
||||
public var count: Int {
|
||||
return self.container.count
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` with the given references.
|
||||
fileprivate init(referencing encoder: __JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.codingPath = codingPath
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - UnkeyedEncodingContainer Methods
|
||||
|
||||
public mutating func encodeNil() throws { self.container.add(NSNull()) }
|
||||
public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) }
|
||||
public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) }
|
||||
|
||||
public mutating func encode(_ value: Float) throws {
|
||||
// Since the float may be invalid and throw, the coding path needs to contain this key.
|
||||
self.encoder.codingPath.append(_JSONKey(index: self.count))
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container.add(try self.encoder.box(value))
|
||||
}
|
||||
|
||||
public mutating func encode(_ value: Double) throws {
|
||||
// Since the double may be invalid and throw, the coding path needs to contain this key.
|
||||
self.encoder.codingPath.append(_JSONKey(index: self.count))
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container.add(try self.encoder.box(value))
|
||||
}
|
||||
|
||||
public mutating func encode<T : Encodable>(_ value: T) throws {
|
||||
self.encoder.codingPath.append(_JSONKey(index: self.count))
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
self.container.add(try self.encoder.box(value))
|
||||
}
|
||||
|
||||
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
|
||||
self.codingPath.append(_JSONKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let dictionary = NSMutableDictionary()
|
||||
self.container.add(dictionary)
|
||||
|
||||
let container = _JSONKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
self.codingPath.append(_JSONKey(index: self.count))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
let array = NSMutableArray()
|
||||
self.container.add(array)
|
||||
return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superEncoder() -> Encoder {
|
||||
return __JSONReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container)
|
||||
}
|
||||
}
|
||||
|
||||
extension __JSONEncoder : SingleValueEncodingContainer {
|
||||
// MARK: - SingleValueEncodingContainer Methods
|
||||
|
||||
fileprivate func assertCanEncodeNewValue() {
|
||||
precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
|
||||
}
|
||||
|
||||
public func encodeNil() throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: NSNull())
|
||||
}
|
||||
|
||||
public func encode(_ value: Bool) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Int64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt8) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt16) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt32) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: UInt64) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: String) throws {
|
||||
assertCanEncodeNewValue()
|
||||
self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Float) throws {
|
||||
assertCanEncodeNewValue()
|
||||
try self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode(_ value: Double) throws {
|
||||
assertCanEncodeNewValue()
|
||||
try self.storage.push(container: self.box(value))
|
||||
}
|
||||
|
||||
public func encode<T : Encodable>(_ value: T) throws {
|
||||
assertCanEncodeNewValue()
|
||||
try self.storage.push(container: self.box(value))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Concrete Value Representations
|
||||
|
||||
extension __JSONEncoder {
|
||||
/// Returns the given value boxed in a container appropriate for pushing onto the container stack.
|
||||
fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) }
|
||||
fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) }
|
||||
|
||||
fileprivate func box(_ float: Float) throws -> NSObject {
|
||||
guard !float.isInfinite && !float.isNaN else {
|
||||
guard case let .convertToString(positiveInfinity: posInfString,
|
||||
negativeInfinity: negInfString,
|
||||
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
|
||||
throw EncodingError._invalidFloatingPointValue(float, at: codingPath)
|
||||
}
|
||||
|
||||
if float == Float.infinity {
|
||||
return NSString(string: posInfString)
|
||||
} else if float == -Float.infinity {
|
||||
return NSString(string: negInfString)
|
||||
} else {
|
||||
return NSString(string: nanString)
|
||||
}
|
||||
}
|
||||
|
||||
return NSNumber(value: float)
|
||||
}
|
||||
|
||||
fileprivate func box(_ double: Double) throws -> NSObject {
|
||||
guard !double.isInfinite && !double.isNaN else {
|
||||
guard case let .convertToString(positiveInfinity: posInfString,
|
||||
negativeInfinity: negInfString,
|
||||
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
|
||||
throw EncodingError._invalidFloatingPointValue(double, at: codingPath)
|
||||
}
|
||||
|
||||
if double == Double.infinity {
|
||||
return NSString(string: posInfString)
|
||||
} else if double == -Double.infinity {
|
||||
return NSString(string: negInfString)
|
||||
} else {
|
||||
return NSString(string: nanString)
|
||||
}
|
||||
}
|
||||
|
||||
return NSNumber(value: double)
|
||||
}
|
||||
|
||||
fileprivate func box(_ date: Date) throws -> NSObject {
|
||||
switch self.options.dateEncodingStrategy {
|
||||
case .deferredToDate:
|
||||
// Must be called with a surrounding with(pushedKey:) call.
|
||||
// Dates encode as single-value objects; this can't both throw and push a container, so no need to catch the error.
|
||||
try date.encode(to: self)
|
||||
return self.storage.popContainer()
|
||||
|
||||
case .secondsSince1970:
|
||||
return NSNumber(value: date.timeIntervalSince1970)
|
||||
|
||||
case .millisecondsSince1970:
|
||||
return NSNumber(value: 1000.0 * date.timeIntervalSince1970)
|
||||
|
||||
case .iso8601:
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
return NSString(string: _iso8601Formatter.string(from: date))
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
|
||||
case .formatted(let formatter):
|
||||
return NSString(string: formatter.string(from: date))
|
||||
|
||||
case .custom(let closure):
|
||||
let depth = self.storage.count
|
||||
do {
|
||||
try closure(date, self)
|
||||
} catch {
|
||||
// If the value pushed a container before throwing, pop it back off to restore state.
|
||||
if self.storage.count > depth {
|
||||
let _ = self.storage.popContainer()
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
guard self.storage.count > depth else {
|
||||
// The closure didn't encode anything. Return the default keyed container.
|
||||
return NSDictionary()
|
||||
}
|
||||
|
||||
// We can pop because the closure encoded something.
|
||||
return self.storage.popContainer()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func box(_ data: Data) throws -> NSObject {
|
||||
switch self.options.dataEncodingStrategy {
|
||||
case .deferredToData:
|
||||
// Must be called with a surrounding with(pushedKey:) call.
|
||||
let depth = self.storage.count
|
||||
do {
|
||||
try data.encode(to: self)
|
||||
} catch {
|
||||
// If the value pushed a container before throwing, pop it back off to restore state.
|
||||
// This shouldn't be possible for Data (which encodes as an array of bytes), but it can't hurt to catch a failure.
|
||||
if self.storage.count > depth {
|
||||
let _ = self.storage.popContainer()
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
return self.storage.popContainer()
|
||||
|
||||
case .base64:
|
||||
return NSString(string: data.base64EncodedString())
|
||||
|
||||
case .blob:
|
||||
return data as NSData
|
||||
|
||||
case .custom(let closure):
|
||||
let depth = self.storage.count
|
||||
do {
|
||||
try closure(data, self)
|
||||
} catch {
|
||||
// If the value pushed a container before throwing, pop it back off to restore state.
|
||||
if self.storage.count > depth {
|
||||
let _ = self.storage.popContainer()
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
guard self.storage.count > depth else {
|
||||
// The closure didn't encode anything. Return the default keyed container.
|
||||
return NSDictionary()
|
||||
}
|
||||
|
||||
// We can pop because the closure encoded something.
|
||||
return self.storage.popContainer()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func box(_ dict: [String : Encodable]) throws -> NSObject? {
|
||||
let depth = self.storage.count
|
||||
let result = self.storage.pushKeyedContainer()
|
||||
do {
|
||||
for (key, value) in dict {
|
||||
self.codingPath.append(_JSONKey(stringValue: key, intValue: nil))
|
||||
defer { self.codingPath.removeLast() }
|
||||
result[key] = try box(value)
|
||||
}
|
||||
} catch {
|
||||
// If the value pushed a container before throwing, pop it back off to restore state.
|
||||
if self.storage.count > depth {
|
||||
let _ = self.storage.popContainer()
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
// The top container should be a new container.
|
||||
guard self.storage.count > depth else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.storage.popContainer()
|
||||
}
|
||||
|
||||
fileprivate func box(_ value: Encodable) throws -> NSObject {
|
||||
return try self.box_(value) ?? NSDictionary()
|
||||
}
|
||||
|
||||
// This method is called "box_" instead of "box" to disambiguate it from the overloads. Because the return type here is different from all of the "box" overloads (and is more general), any "box" calls in here would call back into "box" recursively instead of calling the appropriate overload, which is not what we want.
|
||||
fileprivate func box_(_ value: Encodable) throws -> NSObject? {
|
||||
// Disambiguation between variable and function is required due to
|
||||
// issue tracked at: https://bugs.swift.org/browse/SR-1846
|
||||
let type = Swift.type(of: value)
|
||||
if type == Date.self || type == NSDate.self {
|
||||
// Respect Date encoding strategy
|
||||
return try self.box((value as! Date))
|
||||
} else if type == Data.self || type == NSData.self {
|
||||
// Respect Data encoding strategy
|
||||
return try self.box((value as! Data))
|
||||
} else if type == URL.self || type == NSURL.self {
|
||||
// Encode URLs as single strings.
|
||||
return self.box((value as! URL).absoluteString)
|
||||
} else if type == Decimal.self || type == NSDecimalNumber.self {
|
||||
// JSONSerialization can natively handle NSDecimalNumber.
|
||||
return (value as! NSDecimalNumber)
|
||||
} else if value is _JSONStringDictionaryEncodableMarker {
|
||||
return try self.box(value as! [String : Encodable])
|
||||
} else if let object = value as? NSObject, self.options.passthroughTypeResolver.isPassthroughType(value) {
|
||||
return object
|
||||
}
|
||||
|
||||
// The value should request a container from the __JSONEncoder.
|
||||
let depth = self.storage.count
|
||||
do {
|
||||
try value.encode(to: self)
|
||||
} catch {
|
||||
// If the value pushed a container before throwing, pop it back off to restore state.
|
||||
if self.storage.count > depth {
|
||||
let _ = self.storage.popContainer()
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
// The top container should be a new container.
|
||||
guard self.storage.count > depth else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.storage.popContainer()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - __JSONReferencingEncoder
|
||||
|
||||
/// __JSONReferencingEncoder is a special subclass of __JSONEncoder which has its own storage, but references the contents of a different encoder.
|
||||
/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container).
|
||||
// NOTE: older overlays called this class _JSONReferencingEncoder.
|
||||
// The two must coexist without a conflicting ObjC class name, so it
|
||||
// was renamed. The old name must not be used in the new runtime.
|
||||
fileprivate class __JSONReferencingEncoder : __JSONEncoder {
|
||||
// MARK: Reference types.
|
||||
|
||||
/// The type of container we're referencing.
|
||||
private enum Reference {
|
||||
/// Referencing a specific index in an array container.
|
||||
case array(NSMutableArray, Int)
|
||||
|
||||
/// Referencing a specific key in a dictionary container.
|
||||
case dictionary(NSMutableDictionary, String)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The encoder we're referencing.
|
||||
fileprivate let encoder: __JSONEncoder
|
||||
|
||||
/// The container reference itself.
|
||||
private let reference: Reference
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` by referencing the given array container in the given encoder.
|
||||
fileprivate init(referencing encoder: __JSONEncoder, at index: Int, wrapping array: NSMutableArray) {
|
||||
self.encoder = encoder
|
||||
self.reference = .array(array, index)
|
||||
super.init(options: encoder.options, codingPath: encoder.codingPath)
|
||||
|
||||
self.codingPath.append(_JSONKey(index: index))
|
||||
}
|
||||
|
||||
/// Initializes `self` by referencing the given dictionary container in the given encoder.
|
||||
fileprivate init(referencing encoder: __JSONEncoder,
|
||||
key: CodingKey, convertedKey: __shared CodingKey, wrapping dictionary: NSMutableDictionary) {
|
||||
self.encoder = encoder
|
||||
self.reference = .dictionary(dictionary, convertedKey.stringValue)
|
||||
super.init(options: encoder.options, codingPath: encoder.codingPath)
|
||||
|
||||
self.codingPath.append(key)
|
||||
}
|
||||
|
||||
// MARK: - Coding Path Operations
|
||||
|
||||
fileprivate override var canEncodeNewValue: Bool {
|
||||
// With a regular encoder, the storage and coding path grow together.
|
||||
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
|
||||
// We have to take this into account.
|
||||
return self.storage.count == self.codingPath.count - self.encoder.codingPath.count - 1
|
||||
}
|
||||
|
||||
// MARK: - Deinitialization
|
||||
|
||||
// Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
|
||||
deinit {
|
||||
let value: Any
|
||||
switch self.storage.count {
|
||||
case 0: value = NSDictionary()
|
||||
case 1: value = self.storage.popContainer()
|
||||
default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
|
||||
}
|
||||
|
||||
switch self.reference {
|
||||
case .array(let array, let index):
|
||||
array.insert(value, at: index)
|
||||
|
||||
case .dictionary(let dictionary, let key):
|
||||
dictionary[NSString(string: key)] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// JSON Decoder
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// `JSONDecoder` facilitates the decoding of JSON into semantic `Decodable` types.
|
||||
// NOTE: older overlays had Foundation.JSONDecoder as the ObjC name.
|
||||
// The two must coexist, so it was renamed. The old name must not be
|
||||
// used in the new runtime. _TtC10Foundation13__JSONDecoder is the
|
||||
// mangled name for Foundation.__JSONDecoder.
|
||||
public class FirebaseDataDecoder {
|
||||
// MARK: Options
|
||||
|
||||
/// The strategy to use for decoding `Date` values.
|
||||
public 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(macOS 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: Swift.Decoder) throws -> Date)
|
||||
}
|
||||
|
||||
/// The strategy to use for decoding `Data` values.
|
||||
public 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 an `NSData` blob.
|
||||
case blob
|
||||
|
||||
/// Decode the `Data` as a custom value decoded by the given closure.
|
||||
case custom((_ decoder: Swift.Decoder) throws -> Data)
|
||||
}
|
||||
|
||||
/// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN).
|
||||
public enum NonConformingFloatDecodingStrategy {
|
||||
/// Throw upon encountering non-conforming values. This is the default strategy.
|
||||
case `throw`
|
||||
|
||||
/// Decode the values from the given representation strings.
|
||||
case convertFromString(positiveInfinity: String, negativeInfinity: String, nan: String)
|
||||
}
|
||||
|
||||
/// The strategy to use for automatically changing the value of keys before decoding.
|
||||
public enum KeyDecodingStrategy {
|
||||
/// Use the keys specified by each type. This is the default strategy.
|
||||
case useDefaultKeys
|
||||
|
||||
/// Convert from "snake_case_keys" to "camelCaseKeys" before attempting to match a key with the one specified by each type.
|
||||
///
|
||||
/// The conversion to upper case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences.
|
||||
///
|
||||
/// Converting from snake case to camel case:
|
||||
/// 1. Capitalizes the word starting after each `_`
|
||||
/// 2. Removes all `_`
|
||||
/// 3. Preserves starting and ending `_` (as these are often used to indicate private variables or other metadata).
|
||||
/// For example, `one_two_three` becomes `oneTwoThree`. `_one_two_three_` becomes `_oneTwoThree_`.
|
||||
///
|
||||
/// - Note: Using a key decoding strategy has a nominal performance cost, as each string key has to be inspected for the `_` character.
|
||||
case convertFromSnakeCase
|
||||
|
||||
/// Provide a custom conversion from the key in the encoded JSON to the keys specified by the decoded types.
|
||||
/// The full path to the current decoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before decoding.
|
||||
/// If the result of the conversion is a duplicate key, then only one value will be present in the container for the type to decode from.
|
||||
case custom((_ codingPath: [CodingKey]) -> CodingKey)
|
||||
|
||||
fileprivate static func _convertFromSnakeCase(_ stringKey: String) -> String {
|
||||
guard !stringKey.isEmpty else { return stringKey }
|
||||
|
||||
// Find the first non-underscore character
|
||||
guard let firstNonUnderscore = stringKey.firstIndex(where: { $0 != "_" }) else {
|
||||
// Reached the end without finding an _
|
||||
return stringKey
|
||||
}
|
||||
|
||||
// Find the last non-underscore character
|
||||
var lastNonUnderscore = stringKey.index(before: stringKey.endIndex)
|
||||
while lastNonUnderscore > firstNonUnderscore && stringKey[lastNonUnderscore] == "_" {
|
||||
stringKey.formIndex(before: &lastNonUnderscore)
|
||||
}
|
||||
|
||||
let keyRange = firstNonUnderscore...lastNonUnderscore
|
||||
let leadingUnderscoreRange = stringKey.startIndex..<firstNonUnderscore
|
||||
let trailingUnderscoreRange = stringKey.index(after: lastNonUnderscore)..<stringKey.endIndex
|
||||
|
||||
let components = stringKey[keyRange].split(separator: "_")
|
||||
let joinedString : String
|
||||
if components.count == 1 {
|
||||
// No underscores in key, leave the word as is - maybe already camel cased
|
||||
joinedString = String(stringKey[keyRange])
|
||||
} else {
|
||||
joinedString = ([components[0].lowercased()] + components[1...].map { $0.capitalized }).joined()
|
||||
}
|
||||
|
||||
// Do a cheap isEmpty check before creating and appending potentially empty strings
|
||||
let result : String
|
||||
if (leadingUnderscoreRange.isEmpty && trailingUnderscoreRange.isEmpty) {
|
||||
result = joinedString
|
||||
} else if (!leadingUnderscoreRange.isEmpty && !trailingUnderscoreRange.isEmpty) {
|
||||
// Both leading and trailing underscores
|
||||
result = String(stringKey[leadingUnderscoreRange]) + joinedString + String(stringKey[trailingUnderscoreRange])
|
||||
} else if (!leadingUnderscoreRange.isEmpty) {
|
||||
// Just leading
|
||||
result = String(stringKey[leadingUnderscoreRange]) + joinedString
|
||||
} else {
|
||||
// Just trailing
|
||||
result = joinedString + String(stringKey[trailingUnderscoreRange])
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/// The strategy to use in decoding dates. Defaults to `.deferredToDate`.
|
||||
open var dateDecodingStrategy: DateDecodingStrategy = .deferredToDate
|
||||
|
||||
/// The strategy to use in decoding binary data. Defaults to `.base64`.
|
||||
open var dataDecodingStrategy: DataDecodingStrategy = .base64
|
||||
|
||||
/// The strategy to use in decoding non-conforming numbers. Defaults to `.throw`.
|
||||
open var nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy = .throw
|
||||
|
||||
/// The strategy to use for decoding keys. Defaults to `.useDefaultKeys`.
|
||||
open var keyDecodingStrategy: KeyDecodingStrategy = .useDefaultKeys
|
||||
|
||||
/// A type that can resolve which types to 'pass through' - or leave alone while decoding. Defaults to not passing any types through.
|
||||
open var passthroughTypeResolver: StructureCodingPassthroughTypeResolver.Type = NoPassthroughTypes.self
|
||||
|
||||
/// Contextual user-provided information for use during decoding.
|
||||
open var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
/// Options set on the top-level encoder to pass down the decoding hierarchy.
|
||||
fileprivate struct _Options {
|
||||
let dateDecodingStrategy: DateDecodingStrategy
|
||||
let dataDecodingStrategy: DataDecodingStrategy
|
||||
let nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy
|
||||
let keyDecodingStrategy: KeyDecodingStrategy
|
||||
let passthroughTypeResolver: StructureCodingPassthroughTypeResolver.Type
|
||||
let userInfo: [CodingUserInfoKey : Any]
|
||||
}
|
||||
|
||||
/// The options set on the top-level decoder.
|
||||
fileprivate var options: _Options {
|
||||
return _Options(dateDecodingStrategy: dateDecodingStrategy,
|
||||
dataDecodingStrategy: dataDecodingStrategy,
|
||||
nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy,
|
||||
keyDecodingStrategy: keyDecodingStrategy,
|
||||
passthroughTypeResolver: passthroughTypeResolver,
|
||||
userInfo: userInfo)
|
||||
}
|
||||
|
||||
// MARK: - Constructing a JSON Decoder
|
||||
|
||||
/// Initializes `self` with default strategies.
|
||||
public init() {}
|
||||
|
||||
// MARK: - Decoding Values
|
||||
|
||||
/// Decodes a top-level value of the given type from the given JSON representation.
|
||||
///
|
||||
/// - parameter type: The type of the value to decode.
|
||||
/// - parameter data: The data to decode from.
|
||||
/// - returns: A value of the requested type.
|
||||
/// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not valid JSON.
|
||||
/// - throws: An error if any value throws an error during decoding.
|
||||
open func decode<T : Decodable>(_ type: T.Type, from structure: Any) throws -> T {
|
||||
let decoder = __JSONDecoder(referencing: structure, options: self.options)
|
||||
guard let value = try decoder.unbox(structure, as: type) else {
|
||||
throw Swift.DecodingError.valueNotFound(type, Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value."))
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - __JSONDecoder
|
||||
|
||||
// NOTE: older overlays called this class _JSONDecoder. The two must
|
||||
// coexist without a conflicting ObjC class name, so it was renamed.
|
||||
// The old name must not be used in the new runtime.
|
||||
fileprivate class __JSONDecoder : Decoder {
|
||||
// MARK: Properties
|
||||
|
||||
/// The decoder's storage.
|
||||
fileprivate var storage: _JSONDecodingStorage
|
||||
|
||||
/// Options set on the top-level decoder.
|
||||
fileprivate let options: FirebaseDataDecoder._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 self.options.userInfo
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes `self` with the given top-level container and options.
|
||||
fileprivate init(referencing container: Any, at codingPath: [CodingKey] = [], options: FirebaseDataDecoder._Options) {
|
||||
self.storage = _JSONDecodingStorage()
|
||||
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."))
|
||||
}
|
||||
var topContainer : [String : Any]
|
||||
if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding {
|
||||
guard let top = rcValue.dictionaryValue() else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self,
|
||||
reality: rcValue)
|
||||
}
|
||||
topContainer = top
|
||||
} else {
|
||||
guard let top = self.storage.topContainer as? [String : Any] else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: self.storage.topContainer)
|
||||
}
|
||||
topContainer = top
|
||||
}
|
||||
|
||||
let container = _JSONKeyedDecodingContainer<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."))
|
||||
}
|
||||
|
||||
if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding {
|
||||
guard let arrayValue = rcValue.arrayValue() else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: rcValue)
|
||||
}
|
||||
return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: arrayValue )
|
||||
}
|
||||
|
||||
guard let topContainer = self.storage.topContainer as? [Any] else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: self.storage.topContainer)
|
||||
}
|
||||
|
||||
return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: topContainer)
|
||||
}
|
||||
|
||||
public func singleValueContainer() throws -> SingleValueDecodingContainer {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Decoding Storage
|
||||
|
||||
fileprivate struct _JSONDecodingStorage {
|
||||
// MARK: Properties
|
||||
|
||||
/// The container stack.
|
||||
/// Elements may be any one of the JSON types (NSNull, NSNumber, 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 self.containers.count
|
||||
}
|
||||
|
||||
fileprivate var topContainer: Any {
|
||||
precondition(!self.containers.isEmpty, "Empty container stack.")
|
||||
return self.containers.last!
|
||||
}
|
||||
|
||||
fileprivate mutating func push(container: __owned Any) {
|
||||
self.containers.append(container)
|
||||
}
|
||||
|
||||
fileprivate mutating func popContainer() {
|
||||
precondition(!self.containers.isEmpty, "Empty container stack.")
|
||||
self.containers.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Decoding Containers
|
||||
|
||||
fileprivate struct _JSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
||||
typealias Key = K
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A reference to the decoder we're reading from.
|
||||
private let decoder: __JSONDecoder
|
||||
|
||||
/// 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: __JSONDecoder, wrapping container: [String : Any]) {
|
||||
self.decoder = decoder
|
||||
switch decoder.options.keyDecodingStrategy {
|
||||
case .useDefaultKeys:
|
||||
self.container = container
|
||||
case .convertFromSnakeCase:
|
||||
// Convert the snake case keys in the container to camel case.
|
||||
// If we hit a duplicate key after conversion, then we'll use the first one we saw. Effectively an undefined behavior with JSON dictionaries.
|
||||
self.container = Dictionary(container.map {
|
||||
key, value in (FirebaseDataDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value)
|
||||
}, uniquingKeysWith: { (first, _) in first })
|
||||
case .custom(let converter):
|
||||
self.container = Dictionary(container.map {
|
||||
key, value in (converter(decoder.codingPath + [_JSONKey(stringValue: key, intValue: nil)]).stringValue, value)
|
||||
}, uniquingKeysWith: { (first, _) in first })
|
||||
}
|
||||
self.codingPath = decoder.codingPath
|
||||
}
|
||||
|
||||
// MARK: - KeyedDecodingContainerProtocol Methods
|
||||
|
||||
public var allKeys: [Key] {
|
||||
return self.container.keys.compactMap { Key(stringValue: $0) }
|
||||
}
|
||||
|
||||
public func contains(_ key: Key) -> Bool {
|
||||
return self.container[key.stringValue] != nil
|
||||
}
|
||||
|
||||
private func _errorDescription(of key: CodingKey) -> String {
|
||||
switch decoder.options.keyDecodingStrategy {
|
||||
case .convertFromSnakeCase:
|
||||
// In this case we can attempt to recover the original value by reversing the transform
|
||||
let original = key.stringValue
|
||||
let converted = FirebaseDataEncoder.KeyEncodingStrategy._convertToSnakeCase(original)
|
||||
let roundtrip = FirebaseDataDecoder.KeyDecodingStrategy._convertFromSnakeCase(converted)
|
||||
if converted == original {
|
||||
return "\(key) (\"\(original)\")"
|
||||
} else if roundtrip == original {
|
||||
return "\(key) (\"\(original)\"), converted to \(converted)"
|
||||
} else {
|
||||
return "\(key) (\"\(original)\"), with divergent representation \(roundtrip), converted to \(converted)"
|
||||
}
|
||||
default:
|
||||
// Otherwise, just report the converted string
|
||||
return "\(key) (\"\(key.stringValue)\")"
|
||||
}
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 = self.container[key.stringValue] else {
|
||||
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 = self.container[key.stringValue] else {
|
||||
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
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 {
|
||||
if type is StructureCodingUncodedUnkeyed.Type {
|
||||
// Note: not pushing and popping key to codingPath since the key is
|
||||
// not part of the decoded structure.
|
||||
return try T.init(from: self.decoder)
|
||||
}
|
||||
guard let entry = self.container[key.stringValue] else {
|
||||
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(key)
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let value = try self.decoder.unbox(entry, as: type) 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.keyNotFound(key,
|
||||
DecodingError.Context(codingPath: self.codingPath,
|
||||
debugDescription: "Cannot get \(KeyedDecodingContainer<NestedKey>.self) -- no value found for key \(_errorDescription(of: key))"))
|
||||
}
|
||||
|
||||
guard let dictionary = value as? [String : Any] else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value)
|
||||
}
|
||||
|
||||
let container = _JSONKeyedDecodingContainer<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.keyNotFound(key,
|
||||
DecodingError.Context(codingPath: self.codingPath,
|
||||
debugDescription: "Cannot get UnkeyedDecodingContainer -- no value found for key \(_errorDescription(of: key))"))
|
||||
}
|
||||
|
||||
guard let array = value as? [Any] else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value)
|
||||
}
|
||||
|
||||
return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array)
|
||||
}
|
||||
|
||||
private func _superDecoder(forKey key: __owned CodingKey) throws -> Decoder {
|
||||
self.decoder.codingPath.append(key)
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
let value: Any = self.container[key.stringValue] ?? NSNull()
|
||||
return __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
|
||||
}
|
||||
|
||||
public func superDecoder() throws -> Decoder {
|
||||
return try _superDecoder(forKey: _JSONKey.super)
|
||||
}
|
||||
|
||||
public func superDecoder(forKey key: Key) throws -> Decoder {
|
||||
return try _superDecoder(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct _JSONUnkeyedDecodingContainer : UnkeyedDecodingContainer {
|
||||
// MARK: Properties
|
||||
|
||||
/// A reference to the decoder we're reading from.
|
||||
private let decoder: __JSONDecoder
|
||||
|
||||
/// 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: __JSONDecoder, wrapping container: [Any]) {
|
||||
self.decoder = decoder
|
||||
self.container = container
|
||||
self.codingPath = decoder.codingPath
|
||||
self.currentIndex = 0
|
||||
}
|
||||
|
||||
// MARK: - UnkeyedDecodingContainer Methods
|
||||
|
||||
public var count: Int? {
|
||||
return self.container.count
|
||||
}
|
||||
|
||||
public var isAtEnd: Bool {
|
||||
return self.currentIndex >= self.count!
|
||||
}
|
||||
|
||||
public mutating func decodeNil() throws -> Bool {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
if self.container[self.currentIndex] is NSNull {
|
||||
self.currentIndex += 1
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Bool.Type) throws -> Bool {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Int.Type) throws -> Int {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Int8.Type) throws -> Int8 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Int16.Type) throws -> Int16 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Int32.Type) throws -> Int32 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: Int64.Type) throws -> Int64 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: UInt.Type) throws -> UInt {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: UInt8.Type) throws -> UInt8 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: UInt16.Type) throws -> UInt16 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: UInt32.Type) throws -> UInt32 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return decoded
|
||||
}
|
||||
|
||||
public mutating func decode(_ type: UInt64.Type) throws -> UInt64 {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
|
||||
}
|
||||
|
||||
self.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 + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(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 + [_JSONKey(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 + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(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 + [_JSONKey(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 + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(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 + [_JSONKey(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 + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
|
||||
}
|
||||
|
||||
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
|
||||
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: type) else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(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(_JSONKey(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 {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value)
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
let container = _JSONKeyedDecodingContainer<NestedKey>(referencing: self.decoder, wrapping: dictionary)
|
||||
return KeyedDecodingContainer(container)
|
||||
}
|
||||
|
||||
public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
self.decoder.codingPath.append(_JSONKey(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 keyed 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 {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value)
|
||||
}
|
||||
|
||||
self.currentIndex += 1
|
||||
return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array)
|
||||
}
|
||||
|
||||
public mutating func superDecoder() throws -> Decoder {
|
||||
self.decoder.codingPath.append(_JSONKey(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 __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
|
||||
}
|
||||
}
|
||||
|
||||
extension __JSONDecoder : SingleValueDecodingContainer {
|
||||
// MARK: SingleValueDecodingContainer Methods
|
||||
|
||||
private func expectNonNull<T>(_ type: T.Type) throws {
|
||||
guard !self.decodeNil() else {
|
||||
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected \(type) but found null value instead."))
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeNil() -> Bool {
|
||||
return self.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(type)
|
||||
return try self.unbox(self.storage.topContainer, as: type)!
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Concrete Value Representations
|
||||
|
||||
extension __JSONDecoder {
|
||||
/// Returns the given value unboxed from a container.
|
||||
fileprivate func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
|
||||
return rcValue.boolValue()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fileprivate func rcValNumberAdaptor(_ value: Any) -> Any {
|
||||
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
|
||||
return rcValue.numberValue()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fileprivate func getNumber(_ value: Any, as type: Any.Type) throws -> NSNumber {
|
||||
let val = rcValNumberAdaptor(value)
|
||||
guard let number = val as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: val)
|
||||
}
|
||||
return number
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Int.Type) throws -> Int? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let number = try getNumber(value, as: type)
|
||||
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
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Float.Type) throws -> Float? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let val = rcValNumberAdaptor(value)
|
||||
if let number = val 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
|
||||
*/
|
||||
|
||||
} else if let string = val as? String,
|
||||
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
|
||||
if string == posInfString {
|
||||
return Float.infinity
|
||||
} else if string == negInfString {
|
||||
return -Float.infinity
|
||||
} else if string == nanString {
|
||||
return Float.nan
|
||||
}
|
||||
}
|
||||
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Double.Type) throws -> Double? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let val = rcValNumberAdaptor(value)
|
||||
if let number = val 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
|
||||
*/
|
||||
|
||||
} else if let string = val as? String,
|
||||
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
|
||||
if string == posInfString {
|
||||
return Double.infinity
|
||||
} else if string == negInfString {
|
||||
return -Double.infinity
|
||||
} else if string == nanString {
|
||||
return Double.nan
|
||||
}
|
||||
}
|
||||
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: String.Type) throws -> String? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
|
||||
return rcValue.stringValue()
|
||||
}
|
||||
guard let string = value as? String else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
}
|
||||
|
||||
return string
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Date.Type) throws -> Date? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
switch self.options.dateDecodingStrategy {
|
||||
case .deferredToDate:
|
||||
self.storage.push(container: value)
|
||||
defer { self.storage.popContainer() }
|
||||
return try Date(from: self)
|
||||
|
||||
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(macOS 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)
|
||||
defer { self.storage.popContainer() }
|
||||
return try closure(self)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Data.Type) throws -> Data? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
|
||||
return rcValue.dataValue()
|
||||
}
|
||||
|
||||
switch self.options.dataDecodingStrategy {
|
||||
case .deferredToData:
|
||||
self.storage.push(container: value)
|
||||
defer { self.storage.popContainer() }
|
||||
return try Data(from: self)
|
||||
|
||||
case .base64:
|
||||
guard let string = value as? String else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
}
|
||||
|
||||
guard let data = Data(base64Encoded: string) else {
|
||||
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Encountered Data is not valid Base64."))
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
case .blob:
|
||||
if let data = value as? Data {
|
||||
return data
|
||||
} else if let string = value as? String, let data = Data(base64Encoded: string) {
|
||||
// Support implicit migration of data that was written with .base64 (String type) using
|
||||
// Firestore 10.0 through 10.3.
|
||||
return data
|
||||
}
|
||||
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
|
||||
case .custom(let closure):
|
||||
self.storage.push(container: value)
|
||||
defer { self.storage.popContainer() }
|
||||
return try closure(self)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func unbox(_ value: Any, as type: Decimal.Type) throws -> Decimal? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
let val = rcValNumberAdaptor(value)
|
||||
|
||||
// Attempt to bridge from NSDecimalNumber.
|
||||
if let decimal = val as? Decimal {
|
||||
return decimal
|
||||
} else {
|
||||
let doubleValue = try self.unbox(val, as: Double.self)!
|
||||
return Decimal(doubleValue)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func unbox<T>(_ value: Any, as type: _JSONStringDictionaryDecodableMarker.Type) throws -> T? {
|
||||
guard !(value is NSNull) else { return nil }
|
||||
|
||||
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
|
||||
guard let dictionaryValue = rcValue.dictionaryValue() else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: rcValue)
|
||||
}
|
||||
return dictionaryValue as? T
|
||||
}
|
||||
|
||||
var result = [String : Any]()
|
||||
guard let dict = value as? NSDictionary else {
|
||||
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||
}
|
||||
let elementType = type.elementType
|
||||
for (key, value) in dict {
|
||||
let key = key as! String
|
||||
self.codingPath.append(_JSONKey(stringValue: key, intValue: nil))
|
||||
defer { self.codingPath.removeLast() }
|
||||
|
||||
result[key] = try unbox_(value, as: elementType)
|
||||
}
|
||||
|
||||
return result as? T
|
||||
}
|
||||
|
||||
fileprivate func unbox<T : Decodable>(_ value: Any, as type: T.Type) throws -> T? {
|
||||
return try unbox_(value, as: type) as? T
|
||||
}
|
||||
|
||||
fileprivate func unbox_(_ value: Any, as _type: Decodable.Type) throws -> Any? {
|
||||
if _type == Date.self || _type == NSDate.self {
|
||||
return try self.unbox(value, as: Date.self)
|
||||
} else if _type == Data.self || _type == NSData.self {
|
||||
return try self.unbox(value, as: Data.self)
|
||||
} else if _type == URL.self || _type == 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."))
|
||||
}
|
||||
return url
|
||||
} else if _type == Decimal.self || _type == NSDecimalNumber.self {
|
||||
return try self.unbox(value, as: Decimal.self)
|
||||
} else if let stringKeyedDictType = _type as? _JSONStringDictionaryDecodableMarker.Type {
|
||||
return try self.unbox(value, as: stringKeyedDictType)
|
||||
} else {
|
||||
self.storage.push(container: value)
|
||||
defer { self.storage.popContainer() }
|
||||
if self.options.passthroughTypeResolver.isPassthroughType(value) && type(of: value) == _type {
|
||||
return value
|
||||
}
|
||||
return try _type.init(from: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Shared Key Types
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
fileprivate struct _JSONKey : CodingKey {
|
||||
public var stringValue: String
|
||||
public var intValue: Int?
|
||||
|
||||
public init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = nil
|
||||
}
|
||||
|
||||
public init?(intValue: Int) {
|
||||
self.stringValue = "\(intValue)"
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
public init(stringValue: String, intValue: Int?) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
fileprivate init(index: Int) {
|
||||
self.stringValue = "Index \(index)"
|
||||
self.intValue = index
|
||||
}
|
||||
|
||||
fileprivate static let `super` = _JSONKey(stringValue: "super")!
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Shared ISO8601 Date Formatter
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS.
|
||||
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||
fileprivate var _iso8601Formatter: ISO8601DateFormatter = {
|
||||
let formatter = ISO8601DateFormatter()
|
||||
formatter.formatOptions = .withInternetDateTime
|
||||
return formatter
|
||||
}()
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Error Utilities
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
extension EncodingError {
|
||||
/// Returns a `.invalidValue` error describing the given invalid floating-point value.
|
||||
///
|
||||
///
|
||||
/// - parameter value: The value that was invalid to encode.
|
||||
/// - parameter path: The path of `CodingKey`s taken to encode this value.
|
||||
/// - returns: An `EncodingError` with the appropriate path and debug description.
|
||||
fileprivate static func _invalidFloatingPointValue<T : FloatingPoint>(_ value: T, at codingPath: [CodingKey]) -> EncodingError {
|
||||
let valueDescription: String
|
||||
if value == T.infinity {
|
||||
valueDescription = "\(T.self).infinity"
|
||||
} else if value == -T.infinity {
|
||||
valueDescription = "-\(T.self).infinity"
|
||||
} else {
|
||||
valueDescription = "\(T.self).nan"
|
||||
}
|
||||
|
||||
let debugDescription = "Unable to encode \(valueDescription) directly in JSON. Use JSONEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded."
|
||||
return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription))
|
||||
}
|
||||
}
|
||||
297
Pods/FirebaseSharedSwift/README.md
generated
Normal file
297
Pods/FirebaseSharedSwift/README.md
generated
Normal file
@@ -0,0 +1,297 @@
|
||||
<p align="center">
|
||||
<a href="https://cocoapods.org/pods/Firebase">
|
||||
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=CocoaPods"/>
|
||||
</a>
|
||||
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=Swift%20Package%20Index&color=red"/>
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/Firebase">
|
||||
<img src="https://img.shields.io/github/license/Firebase/firebase-ios-sdk?style=flat"/>
|
||||
</a><br/>
|
||||
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dplatforms"/>
|
||||
</a>
|
||||
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dswift-versions"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Firebase Apple Open Source Development
|
||||
|
||||
This repository contains the source code for all Apple platform Firebase SDKs except FirebaseAnalytics.
|
||||
|
||||
Firebase is an app development platform with tools to help you build, grow, and
|
||||
monetize your app. More information about Firebase can be found on the
|
||||
[official Firebase website](https://firebase.google.com).
|
||||
|
||||
## Installation
|
||||
|
||||
See the subsections below for details about the different installation methods. Where
|
||||
available, it's recommended to install any libraries with a `Swift` suffix to get the
|
||||
best experience when writing your app in Swift.
|
||||
|
||||
1. [Standard pod install](#standard-pod-install)
|
||||
2. [Swift Package Manager](#swift-package-manager)
|
||||
3. [Installing from the GitHub repo](#installing-from-github)
|
||||
4. [Experimental Carthage](#carthage-ios-only)
|
||||
|
||||
### Standard pod install
|
||||
|
||||
For instructions on the standard pod install, visit:
|
||||
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
|
||||
found in the [SwiftPackageManager.md](SwiftPackageManager.md) Markdown file.
|
||||
|
||||
### Installing from GitHub
|
||||
|
||||
These instructions can be used to access the Firebase repo at other branches,
|
||||
tags, or commits.
|
||||
|
||||
#### Background
|
||||
|
||||
See [the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
|
||||
for instructions and options about overriding pod source locations.
|
||||
|
||||
#### Accessing Firebase Source Snapshots
|
||||
|
||||
All official releases are tagged in this repo and available via CocoaPods. To access a local
|
||||
source snapshot or unreleased branch, use Podfile directives like the following:
|
||||
|
||||
To access FirebaseFirestore via a branch:
|
||||
```ruby
|
||||
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||
```
|
||||
|
||||
To access FirebaseMessaging via a checked-out version of the firebase-ios-sdk repo:
|
||||
```ruby
|
||||
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
|
||||
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
|
||||
```
|
||||
|
||||
### Carthage (iOS only)
|
||||
|
||||
Instructions for the experimental Carthage distribution can be found at
|
||||
[Carthage.md](Carthage.md).
|
||||
|
||||
### Using Firebase from a Framework or a library
|
||||
|
||||
For details on using Firebase from a Framework or a library, refer to [firebase_in_libraries.md](docs/firebase_in_libraries.md).
|
||||
|
||||
## Development
|
||||
|
||||
To develop Firebase software in this repository, ensure that you have at least
|
||||
the following software:
|
||||
|
||||
* Xcode 14.1 (or later)
|
||||
|
||||
CocoaPods is still the canonical way to develop, but much of the repo now supports
|
||||
development with Swift Package Manager.
|
||||
|
||||
### CocoaPods
|
||||
|
||||
Install the following:
|
||||
* CocoaPods 1.12.0 (or later)
|
||||
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
|
||||
|
||||
For the pod that you want to develop:
|
||||
|
||||
```ruby
|
||||
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
|
||||
```
|
||||
|
||||
Note: If the CocoaPods cache is out of date, you may need to run
|
||||
`pod repo update` before the `pod gen` command.
|
||||
|
||||
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
|
||||
those platforms. Since 10.2, Xcode does not properly handle multi-platform
|
||||
CocoaPods workspaces.
|
||||
|
||||
Firestore has a self-contained Xcode project. See
|
||||
[Firestore/README](Firestore/README.md) Markdown file.
|
||||
|
||||
#### Development for Catalyst
|
||||
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
|
||||
* Check the Mac box in the App-iOS Build Settings
|
||||
* Sign the App in the Settings Signing & Capabilities tab
|
||||
* Click Pods in the Project Manager
|
||||
* Add Signing to the iOS host app and unit test targets
|
||||
* Select the Unit-unit scheme
|
||||
* Run it to build and test
|
||||
|
||||
Alternatively, disable signing in each target:
|
||||
* Go to Build Settings tab
|
||||
* Click `+`
|
||||
* Select `Add User-Defined Setting`
|
||||
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
|
||||
|
||||
### Swift Package Manager
|
||||
* To enable test schemes: `./scripts/setup_spm_tests.sh`
|
||||
* `open Package.swift` or double click `Package.swift` in Finder.
|
||||
* Xcode will open the project
|
||||
* Choose a scheme for a library to build or test suite to run
|
||||
* Choose a target platform by selecting the run destination along with the scheme
|
||||
|
||||
### Adding a New Firebase Pod
|
||||
|
||||
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
|
||||
|
||||
### Managing Headers and Imports
|
||||
|
||||
For information about managing headers and imports, see [HeadersImports](HeadersImports.md) Markdown file.
|
||||
|
||||
### Code Formatting
|
||||
|
||||
To ensure that the code is formatted consistently, run the script
|
||||
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
|
||||
before creating a pull request (PR).
|
||||
|
||||
GitHub Actions will verify that any code changes are done in a style-compliant
|
||||
way. Install `clang-format` and `mint`:
|
||||
|
||||
```console
|
||||
brew install clang-format@18
|
||||
brew install mint
|
||||
```
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||
|
||||
### Running Sample Apps
|
||||
To run the sample apps and integration tests, you'll need a valid
|
||||
`GoogleService-Info.plist
|
||||
` file. The Firebase Xcode project contains dummy plist
|
||||
files without real values, but they can be replaced with real plist files. To get your own
|
||||
`GoogleService-Info.plist` files:
|
||||
|
||||
1. Go to the [Firebase Console](https://console.firebase.google.com/)
|
||||
2. Create a new Firebase project, if you don't already have one
|
||||
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||
identifier (e.g., `com.google.Database-Example`)
|
||||
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||
|
||||
### Coverage Report Generation
|
||||
|
||||
For coverage report generation instructions, see [scripts/code_coverage_report/README](scripts/code_coverage_report/README.md) Markdown file.
|
||||
|
||||
## Specific Component Instructions
|
||||
See the sections below for any special instructions for those components.
|
||||
|
||||
### Firebase Auth
|
||||
|
||||
For specific Firebase Auth development, refer to the [Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about
|
||||
building and running the FirebaseAuth pod along with various samples and tests.
|
||||
|
||||
### Firebase Database
|
||||
|
||||
The Firebase Database Integration tests can be run against a locally running Database Emulator
|
||||
or against a production instance.
|
||||
|
||||
To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
|
||||
running the integration test.
|
||||
|
||||
To run against a production instance, provide a valid `GoogleServices-Info.plist` and copy it to
|
||||
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||
running.
|
||||
|
||||
### Firebase Dynamic Links
|
||||
|
||||
Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
|
||||
|
||||
Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
|
||||
|
||||
### Firebase Performance Monitoring
|
||||
|
||||
For specific Firebase Performance Monitoring development, see
|
||||
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
|
||||
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
|
||||
integrating Performance with the dev test App.
|
||||
|
||||
### Firebase Storage
|
||||
|
||||
To run the Storage Integration tests, follow the instructions in
|
||||
[StorageIntegration.swift](FirebaseStorage/Tests/Integration/StorageIntegration.swift).
|
||||
|
||||
#### Push Notifications
|
||||
|
||||
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
|
||||
In order to test receiving push notifications, you will need to:
|
||||
|
||||
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
|
||||
account and enable that App ID for push notifications.
|
||||
2. You'll also need to
|
||||
[upload your APNs Provider Authentication Key or certificate to the
|
||||
Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
|
||||
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
|
||||
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
|
||||
|
||||
#### iOS Simulator
|
||||
|
||||
The iOS Simulator cannot register for remote notifications and will not receive push notifications.
|
||||
To receive push notifications, follow the steps above and run the app on a physical device.
|
||||
|
||||
## Building with Firebase on Apple platforms
|
||||
|
||||
Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
|
||||
are community supported. Thanks to community contributions for many of the multi-platform PRs.
|
||||
|
||||
At this time, most of Firebase's products are available across Apple platforms. There are still
|
||||
a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
|
||||
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
|
||||
in Firebase's documentation.
|
||||
|
||||
### visionOS
|
||||
|
||||
Where supported, visionOS works as expected with the exception of Firestore via Swift Package
|
||||
Manager where it is required to use the source distribution.
|
||||
|
||||
To enable the Firestore source distribution, quit Xcode and open the desired
|
||||
project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
|
||||
variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
|
||||
To go back to using the binary distribution of Firestore, quit Xcode and open
|
||||
Xcode like normal, without the environment variable.
|
||||
|
||||
### watchOS
|
||||
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
|
||||
work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).
|
||||
|
||||
Keep in mind that watchOS is not officially supported by Firebase. While we can catch basic unit
|
||||
test issues with GitHub Actions, there may be some changes where the SDK no longer works as expected
|
||||
on watchOS. If you encounter this, please
|
||||
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
|
||||
|
||||
During app setup in the console, you may get to a step that mentions something like "Checking if the
|
||||
app has communicated with our servers". This relies on Analytics and will not work on watchOS.
|
||||
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
|
||||
|
||||
#### Additional Crashlytics Notes
|
||||
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
|
||||
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
|
||||
|
||||
## Combine
|
||||
Thanks to contributions from the community, _FirebaseCombineSwift_ contains support for Apple's Combine
|
||||
framework. This module is currently under development and not yet supported for use in production
|
||||
environments. For more details, please refer to the [docs](FirebaseCombineSwift/README.md).
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [Roadmap](ROADMAP.md) for more about the Firebase Apple SDK Open Source
|
||||
plans and directions.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
|
||||
Apple SDK.
|
||||
|
||||
## License
|
||||
|
||||
The contents of this repository are licensed under the
|
||||
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
Your use of Firebase is governed by the
|
||||
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
|
||||
Reference in New Issue
Block a user