This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

View 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.

View 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]?
}

View 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
View 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/).