Skip to content

Commit bc2bc6b

Browse files
authored
Fix RC Codable to handle arrays and dictionaries from plist defaults (#10046)
1 parent cbaa7ce commit bc2bc6b

File tree

9 files changed

+118
-5
lines changed

9 files changed

+118
-5
lines changed

FirebaseRemoteConfigSwift.podspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ app update.
5353
'FirebaseRemoteConfigSwift/Tests/FakeUtils/*.swift',
5454
'FirebaseRemoteConfigSwift/Tests/ObjC/*.[hm]',
5555
]
56+
swift_api.resources = 'FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist'
5657
swift_api.requires_app_host = true
5758
swift_api.pod_target_xcconfig = {
5859
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseRemoteConfigSwift/Tests/ObjC/Bridging-Header.h',
@@ -75,6 +76,7 @@ app update.
7576
'FirebaseRemoteConfigSwift/Tests/FakeConsole/*.swift',
7677
'FirebaseRemoteConfigSwift/Tests/ObjC/*.[hm]',
7778
]
79+
fake_console.resources = 'FirebaseRemoteConfigSwift/Tests/Defaults-testInfo.plist'
7880
fake_console.requires_app_host = true
7981
fake_console.pod_target_xcconfig = {
8082
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseRemoteConfigSwift/Tests/ObjC/Bridging-Header.h',

FirebaseRemoteConfigSwift/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 9.5.0
2+
- [fixed] Fix Codable implementation to handle arrays and dictionaries from plist defaults. (#9980)
3+
14
# 9.0.0
25
- [added] **Breaking change:** `FirebaseRemoteConfigSwift` has exited beta and
36
is now generally available for use.

FirebaseRemoteConfigSwift/Sources/FirebaseRemoteConfigValueDecoderHelper.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,15 @@ struct FirebaseRemoteConfigValueDecoderHelper: FirebaseRemoteConfigValueDecoding
4040
return value.dataValue
4141
}
4242

43-
func jsonValue() -> [String: AnyHashable]? {
43+
func arrayValue() -> [AnyHashable]? {
44+
guard let value = value.jsonValue as? [AnyHashable] else {
45+
return nil
46+
}
47+
return value
48+
}
49+
50+
func dictionaryValue() -> [String: AnyHashable]? {
4451
guard let value = value.jsonValue as? [String: AnyHashable] else {
45-
// nil is the historical behavior for failing to extract JSON.
4652
return nil
4753
}
4854
return value
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>lastCheckTime</key>
6+
<date>2016-02-28T18:33:31Z</date>
7+
<key>isPaidUser</key>
8+
<true/>
9+
<key>dataValue</key>
10+
<data>Mi40</data>
11+
<key>newItem</key>
12+
<real>2.4</real>
13+
<key>Languages</key>
14+
<string>English</string>
15+
<key>FileInfo</key>
16+
<string>To setup default config.</string>
17+
<key>format</key>
18+
<string>key to value.</string>
19+
<key>arrayValue</key>
20+
<array>
21+
<string>foo</string>
22+
<string>bar</string>
23+
<string>baz</string>
24+
</array>
25+
<key>arrayIntValue</key>
26+
<array>
27+
<integer>1</integer>
28+
<integer>2</integer>
29+
<integer>0</integer>
30+
<integer>3</integer>
31+
</array>
32+
<key>dictValue</key>
33+
<dict>
34+
<key>foo</key>
35+
<string>foo</string>
36+
<key>bar</key>
37+
<string>bar</string>
38+
<key>baz</key>
39+
<string>baz</string>
40+
</dict>
41+
</dict>
42+
</plist>

FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,43 @@ import XCTest
170170
}
171171
XCTFail("Failed to throw on missing field")
172172
}
173+
174+
func testCodableAfterPlistDefaults() throws {
175+
struct Defaults: Codable {
176+
let format: String
177+
let isPaidUser: Bool
178+
let newItem: Double
179+
let Languages: String
180+
let dictValue: [String: String]
181+
let arrayValue: [String]
182+
let arrayIntValue: [Int]
183+
}
184+
// setDefaults(fromPlist:) doesn't work because of dynamic linking.
185+
// More details in RCNRemoteConfigTest.m
186+
var findPlist: String?
187+
#if SWIFT_PACKAGE
188+
findPlist = Bundle.module.path(forResource: "Defaults-testInfo", ofType: "plist")
189+
#else
190+
for b in Bundle.allBundles {
191+
findPlist = b.path(forResource: "Defaults-testInfo", ofType: "plist")
192+
if findPlist != nil {
193+
break
194+
}
195+
}
196+
#endif
197+
let plistFile = try XCTUnwrap(findPlist)
198+
let defaults = NSDictionary(contentsOfFile: plistFile)
199+
config.setDefaults(defaults as? [String: NSObject])
200+
let readDefaults: Defaults = try config.decoded()
201+
XCTAssertEqual(readDefaults.format, "key to value.")
202+
XCTAssertEqual(readDefaults.isPaidUser, true)
203+
XCTAssertEqual(readDefaults.newItem, 2.4)
204+
XCTAssertEqual(readDefaults.Languages, "English")
205+
XCTAssertEqual(readDefaults.dictValue, ["foo": "foo",
206+
"bar": "bar",
207+
"baz": "baz"])
208+
XCTAssertEqual(readDefaults.arrayValue, ["foo", "bar", "baz"])
209+
XCTAssertEqual(readDefaults.arrayIntValue, [1, 2, 0, 3])
210+
}
173211
}
174212
#endif

FirebaseSharedSwift/Sources/FirebaseRemoteConfigValueDecoding.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ public protocol FirebaseRemoteConfigValueDecoding {
2020
func boolValue() -> Bool
2121
func stringValue() -> String
2222
func dataValue() -> Data
23-
func jsonValue() -> [String: AnyHashable]?
23+
func arrayValue() -> [AnyHashable]?
24+
func dictionaryValue() -> [String: AnyHashable]?
2425
}

FirebaseSharedSwift/Sources/third_party/FirebaseDataEncoder/FirebaseDataEncoder.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,11 @@ fileprivate class __JSONDecoder : Decoder {
12521252
debugDescription: "Cannot get keyed decoding container -- found null value instead."))
12531253
}
12541254
var topContainer : [String : Any]
1255-
if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding,
1256-
let top = rcValue.jsonValue() {
1255+
if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding {
1256+
guard let top = rcValue.dictionaryValue() else {
1257+
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self,
1258+
reality: rcValue)
1259+
}
12571260
topContainer = top
12581261
} else {
12591262
guard let top = self.storage.topContainer as? [String : Any] else {
@@ -1273,6 +1276,13 @@ fileprivate class __JSONDecoder : Decoder {
12731276
debugDescription: "Cannot get unkeyed decoding container -- found null value instead."))
12741277
}
12751278

1279+
if let rcValue = self.storage.topContainer as? FirebaseRemoteConfigValueDecoding {
1280+
guard let arrayValue = rcValue.arrayValue() else {
1281+
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: rcValue)
1282+
}
1283+
return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: arrayValue )
1284+
}
1285+
12761286
guard let topContainer = self.storage.topContainer as? [Any] else {
12771287
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: self.storage.topContainer)
12781288
}
@@ -2468,6 +2478,13 @@ extension __JSONDecoder {
24682478
fileprivate func unbox<T>(_ value: Any, as type: _JSONStringDictionaryDecodableMarker.Type) throws -> T? {
24692479
guard !(value is NSNull) else { return nil }
24702480

2481+
if let rcValue = value as? FirebaseRemoteConfigValueDecoding {
2482+
guard let dictionaryValue = rcValue.dictionaryValue() else {
2483+
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: rcValue)
2484+
}
2485+
return dictionaryValue as? T
2486+
}
2487+
24712488
var result = [String : Any]()
24722489
guard let dict = value as? NSDictionary else {
24732490
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,9 @@ let package = Package(
10601060
"README.md",
10611061
"ObjC/",
10621062
],
1063+
resources: [
1064+
.process("Defaults-testInfo.plist"),
1065+
],
10631066
cSettings: [
10641067
.headerSearchPath("../../"),
10651068
]

scripts/localize_podfile.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ let implicitPods = [
3939
"FirebaseCoreExtension", "FirebaseAppCheckInterop",
4040
"FirebaseAuthInterop", "FirebaseMessagingInterop",
4141
"FirebaseStorageInternal", "FirebaseCoreInternal",
42+
"FirebaseSharedSwift",
4243
]
4344

4445
let binaryPods = [

0 commit comments

Comments
 (0)