Skip to content

Commit 8ac886d

Browse files
authored
Consistent Collections Coding in FunctionsSerializer (#13419)
1 parent 6134af2 commit 8ac886d

File tree

2 files changed

+66
-40
lines changed

2 files changed

+66
-40
lines changed

FirebaseFunctions/Sources/Internal/FunctionsSerializer.swift

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,16 @@ class FunctionsSerializer: NSObject {
4747
return object as AnyObject
4848
} else if object is NSDictionary {
4949
let dict = object as! NSDictionary
50-
let encoded: NSMutableDictionary = .init()
51-
dict.enumerateKeysAndObjects { key, obj, _ in
52-
// TODO(wilsonryan): Not exact translation
53-
let anyObj = obj as AnyObject
54-
let stringKey = key as! String
55-
let value = try! encode(anyObj)
56-
encoded[stringKey] = value
50+
let encoded = NSMutableDictionary()
51+
try dict.forEach { key, value in
52+
encoded[key] = try encode(value)
5753
}
5854
return encoded
5955
} else if object is NSArray {
6056
let array = object as! NSArray
6157
let encoded = NSMutableArray()
62-
for item in array {
63-
let anyItem = item as AnyObject
64-
let encodedItem = try encode(anyItem)
65-
encoded.add(encodedItem)
58+
try array.forEach { element in
59+
try encoded.add(encode(element))
6660
}
6761
return encoded
6862

@@ -91,14 +85,11 @@ class FunctionsSerializer: NSObject {
9185
}
9286
return decoded
9387
} else if let array = object as? NSArray {
94-
let result = NSMutableArray(capacity: array.count)
95-
for obj in array {
96-
// TODO: Is this data loss? The API is a bit weird.
97-
if let decoded = try decode(obj) {
98-
result.add(decoded)
99-
}
88+
let decoded = NSMutableArray(capacity: array.count)
89+
try array.forEach { element in
90+
try decoded.add(decode(element) as Any)
10091
}
101-
return result
92+
return decoded
10293
} else if object is NSNumber || object is NSString || object is NSNull {
10394
return object as AnyObject
10495
}

FirebaseFunctions/Tests/Unit/FunctionsSerializerTests.swift

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ class FunctionsSerializerTests: XCTestCase {
187187
XCTAssertEqual(input, try serializer.encode(input) as? NSArray)
188188
}
189189

190+
func testEncodeArrayWithInvalidElements() {
191+
let input = ["TEST", CustomObject()] as NSArray
192+
193+
try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
194+
}
195+
190196
func testDecodeArray() throws {
191197
let input = [
192198
1 as Int64,
@@ -198,7 +204,13 @@ class FunctionsSerializerTests: XCTestCase {
198204
XCTAssertEqual(expected, try serializer.decode(input) as? NSArray)
199205
}
200206

201-
func testEncodeMap() {
207+
func testDecodeArrayWithInvalidElements() {
208+
let input = ["TEST", CustomObject()] as NSArray
209+
210+
try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
211+
}
212+
213+
func testEncodeDictionary() throws {
202214
let input = [
203215
"foo": 1 as Int32,
204216
"bar": "hello",
@@ -213,12 +225,38 @@ class FunctionsSerializerTests: XCTestCase {
213225
XCTAssertEqual(expected, try serializer.encode(input) as? NSDictionary)
214226
}
215227

216-
func testDecodeMap() {
228+
func testEncodeDictionaryWithInvalidElements() {
229+
let input = ["TEST_CustomObj": CustomObject()] as NSDictionary
230+
231+
try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
232+
}
233+
234+
func testEncodeDictionaryWithInvalidNestedDictionary() {
235+
let input =
236+
["TEST_NestedDict": ["TEST_CustomObj": CustomObject()] as NSDictionary] as NSDictionary
237+
238+
try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
239+
}
240+
241+
func testDecodeDictionary() throws {
217242
let input = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
218243
let expected = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
219244
XCTAssertEqual(expected, try serializer.decode(input) as? NSDictionary)
220245
}
221246

247+
func testDecodeDictionaryWithInvalidElements() {
248+
let input = ["TEST_CustomObj": CustomObject()] as NSDictionary
249+
250+
try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
251+
}
252+
253+
func testDecodeDictionaryWithInvalidNestedDictionary() {
254+
let input =
255+
["TEST_NestedDict": ["TEST_CustomObj": CustomObject()] as NSDictionary] as NSDictionary
256+
257+
try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
258+
}
259+
222260
func testEncodeUnknownType() {
223261
let input = ["@type": "unknown", "value": "whatever"] as NSDictionary
224262
XCTAssertEqual(input, try serializer.encode(input) as? NSDictionary)
@@ -237,37 +275,34 @@ class FunctionsSerializerTests: XCTestCase {
237275
func testEncodeUnsupportedType() {
238276
let input = CustomObject()
239277

240-
do {
241-
let _ = try serializer.encode(input)
242-
XCTFail("Expected an error")
243-
} catch {
244-
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer.Error
245-
else {
246-
return XCTFail("Unexpected error: \(error)")
247-
}
248-
249-
XCTAssertEqual(typeName, "CustomObject")
250-
}
278+
try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
251279
}
252280

253281
func testDecodeUnsupportedType() {
254282
let input = CustomObject()
255283

256-
do {
257-
let _ = try serializer.decode(input)
258-
XCTFail("Expected an error")
259-
} catch {
260-
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer.Error
261-
else {
262-
return XCTFail("Unexpected error: \(error)")
284+
try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
285+
}
286+
}
287+
288+
// MARK: - Utilities
289+
290+
extension FunctionsSerializerTests {
291+
private func assert<T>(_ expression: @autoclosure () throws -> T,
292+
throwsUnsupportedTypeErrorWithName expectedTypeName: String,
293+
line: UInt = #line) {
294+
XCTAssertThrowsError(try expression(), line: line) { error in
295+
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer
296+
.Error else {
297+
return XCTFail("Unexpected error: \(error)", line: line)
263298
}
264299

265-
XCTAssertEqual(typeName, "CustomObject")
300+
XCTAssertEqual(typeName, expectedTypeName, line: line)
266301
}
267302
}
268303
}
269304

270305
/// Used to represent a type that cannot be encoded or decoded.
271-
private struct CustomObject {
306+
private class CustomObject {
272307
let id = 123
273308
}

0 commit comments

Comments
 (0)