diff --git a/FirebaseRemoteConfigSwift.podspec b/FirebaseRemoteConfigSwift.podspec
index 02f5dd07f2..152dd42b13 100644
--- a/FirebaseRemoteConfigSwift.podspec
+++ b/FirebaseRemoteConfigSwift.podspec
@@ -53,6 +53,7 @@ app update.
'FirebaseRemoteConfigSwift/Tests/FakeUtils/*.swift',
'FirebaseRemoteConfigSwift/Tests/ObjC/*.[hm]',
]
+ swift_api.resources = 'FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist'
swift_api.requires_app_host = true
swift_api.pod_target_xcconfig = {
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseRemoteConfigSwift/Tests/ObjC/Bridging-Header.h',
@@ -75,6 +76,7 @@ app update.
'FirebaseRemoteConfigSwift/Tests/FakeConsole/*.swift',
'FirebaseRemoteConfigSwift/Tests/ObjC/*.[hm]',
]
+ fake_console.resources = 'FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist'
fake_console.requires_app_host = true
fake_console.pod_target_xcconfig = {
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseRemoteConfigSwift/Tests/ObjC/Bridging-Header.h',
diff --git a/FirebaseRemoteConfigSwift/CHANGELOG.md b/FirebaseRemoteConfigSwift/CHANGELOG.md
index 82cfd078f5..a023d6e841 100644
--- a/FirebaseRemoteConfigSwift/CHANGELOG.md
+++ b/FirebaseRemoteConfigSwift/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 9.5.0
+- [fixed] Fix Codable implementation to handle arrays and dictionaries from plist defaults. (#9980)
+
# 9.0.0
- [added] **Breaking change:** `FirebaseRemoteConfigSwift` has exited beta and
is now generally available for use.
diff --git a/FirebaseRemoteConfigSwift/Sources/FirebaseRemoteConfigValueDecoderHelper.swift b/FirebaseRemoteConfigSwift/Sources/FirebaseRemoteConfigValueDecoderHelper.swift
index 44d892ad4c..2a7b79a135 100644
--- a/FirebaseRemoteConfigSwift/Sources/FirebaseRemoteConfigValueDecoderHelper.swift
+++ b/FirebaseRemoteConfigSwift/Sources/FirebaseRemoteConfigValueDecoderHelper.swift
@@ -40,9 +40,15 @@ struct FirebaseRemoteConfigValueDecoderHelper: FirebaseRemoteConfigValueDecoding
return value.dataValue
}
- func jsonValue() -> [String: AnyHashable]? {
+ func arrayValue() -> [AnyHashable]? {
+ guard let value = value.jsonValue as? [AnyHashable] else {
+ return nil
+ }
+ return value
+ }
+
+ func dictionaryValue() -> [String: AnyHashable]? {
guard let value = value.jsonValue as? [String: AnyHashable] else {
- // nil is the historical behavior for failing to extract JSON.
return nil
}
return value
diff --git a/FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist b/FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist
new file mode 100644
index 0000000000..784dcb4a40
--- /dev/null
+++ b/FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ lastCheckTime
+ 2016-02-28T18:33:31Z
+ isPaidUser
+
+ dataValue
+ Mi40
+ newItem
+ 2.4
+ Languages
+ English
+ FileInfo
+ To setup default config.
+ format
+ key to value.
+ arrayValue
+
+ foo
+ bar
+ baz
+
+ arrayIntValue
+
+ 1
+ 2
+ 0
+ 3
+
+ dictValue
+
+ foo
+ foo
+ bar
+ bar
+ baz
+ baz
+
+
+
diff --git a/FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift b/FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift
index 21a888d7f5..5ebf90dfe0 100644
--- a/FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift
+++ b/FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift
@@ -170,5 +170,43 @@ import XCTest
}
XCTFail("Failed to throw on missing field")
}
+
+ func testCodableAfterPlistDefaults() throws {
+ struct Defaults: Codable {
+ let format: String
+ let isPaidUser: Bool
+ let newItem: Double
+ let Languages: String
+ let dictValue: [String: String]
+ let arrayValue: [String]
+ let arrayIntValue: [Int]
+ }
+ // setDefaults(fromPlist:) doesn't work because of dynamic linking.
+ // More details in RCNRemoteConfigTest.m
+ var findPlist: String?
+ #if SWIFT_PACKAGE
+ findPlist = Bundle.module.path(forResource: "Defaults-testInfo", ofType: "plist")
+ #else
+ for b in Bundle.allBundles {
+ findPlist = b.path(forResource: "Defaults-testInfo", ofType: "plist")
+ if findPlist != nil {
+ break
+ }
+ }
+ #endif
+ let plistFile = try XCTUnwrap(findPlist)
+ let defaults = NSDictionary(contentsOfFile: plistFile)
+ config.setDefaults(defaults as? [String: NSObject])
+ let readDefaults: Defaults = try config.decoded()
+ XCTAssertEqual(readDefaults.format, "key to value.")
+ XCTAssertEqual(readDefaults.isPaidUser, true)
+ XCTAssertEqual(readDefaults.newItem, 2.4)
+ XCTAssertEqual(readDefaults.Languages, "English")
+ XCTAssertEqual(readDefaults.dictValue, ["foo": "foo",
+ "bar": "bar",
+ "baz": "baz"])
+ XCTAssertEqual(readDefaults.arrayValue, ["foo", "bar", "baz"])
+ XCTAssertEqual(readDefaults.arrayIntValue, [1, 2, 0, 3])
+ }
}
#endif
diff --git a/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift b/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift
index da6c446784..6d81d90e19 100644
--- a/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift
+++ b/FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift
@@ -20,5 +20,6 @@ public protocol FirebaseRemoteConfigValueDecoding {
func boolValue() -> Bool
func stringValue() -> String
func dataValue() -> Data
- func jsonValue() -> [String: AnyHashable]?
+ func arrayValue() -> [AnyHashable]?
+ func dictionaryValue() -> [String: AnyHashable]?
}
diff --git a/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift b/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift
index 03788f712a..215e0abfab 100644
--- a/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift
+++ b/FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift
@@ -1252,8 +1252,11 @@ fileprivate class __JSONDecoder : Decoder {
debugDescription: "Cannot get keyed decoding container -- found null value instead."))
}
var topContainer : [String : Any]
- if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding,
- let top = rcValue.jsonValue() {
+ 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 {
@@ -1273,6 +1276,13 @@ fileprivate class __JSONDecoder : Decoder {
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)
}
@@ -2468,6 +2478,13 @@ extension __JSONDecoder {
fileprivate func unbox(_ 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)
diff --git a/Package.swift b/Package.swift
index 72722c80b3..14353bbd25 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1060,6 +1060,9 @@ let package = Package(
"README.md",
"ObjC/",
],
+ resources: [
+ .process("Defaults-testInfo.plist"),
+ ],
cSettings: [
.headerSearchPath("../../"),
]
diff --git a/scripts/localize_podfile.swift b/scripts/localize_podfile.swift
index 2a8fca2c19..c111312d32 100755
--- a/scripts/localize_podfile.swift
+++ b/scripts/localize_podfile.swift
@@ -39,6 +39,7 @@ let implicitPods = [
"FirebaseCoreExtension", "FirebaseAppCheckInterop",
"FirebaseAuthInterop", "FirebaseMessagingInterop",
"FirebaseStorageInternal", "FirebaseCoreInternal",
+ "FirebaseSharedSwift",
]
let binaryPods = [