Skip to content

Commit

Permalink
fix: support json array in attributes (#358)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrehan27 committed Jul 26, 2023
1 parent 278f0f1 commit a634358
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 23 deletions.
25 changes: 17 additions & 8 deletions Sources/Common/Util/StringAnyEncodable.swift
@@ -1,22 +1,31 @@
import Foundation

public struct StringAnyEncodable: Encodable {
private let logger: Logger
private let data: [String: AnyEncodable]

public init(_ data: [String: Any]) {
var builtValue = [String: AnyEncodable]()
for (key, value) in data {
public init(logger: Logger, _ data: [String: Any]) {
// Nested function to convert the ‘Any’ values to ‘AnyEncodable’ recursively
func encode(value: Any) -> AnyEncodable? {
switch value {
case let enc as Encodable:
builtValue[key] = AnyEncodable(enc)
return AnyEncodable(enc)

case let dict as [String: Any]:
builtValue[key] = AnyEncodable(StringAnyEncodable(dict))
return AnyEncodable(StringAnyEncodable(logger: logger, dict))

case let list as [Any]:
// If the value is an array, recursively encode each element
return AnyEncodable(list.compactMap { encode(value: $0) })

default:
// XXX: logger error
continue
logger.error("Tried to convert \(data) into [String: AnyEncodable] but the data type is not Encodable.")
return nil
}
}
self.data = builtValue

self.logger = logger
self.data = data.compactMapValues { encode(value: $0) }
}

public func encode(to encoder: Encoder) throws {
Expand Down
7 changes: 5 additions & 2 deletions Sources/Tracking/CustomerIO.swift
Expand Up @@ -356,7 +356,10 @@ public class CustomerIO: CustomerIOInstance {
name: String,
data: [String: Any]
) {
automaticScreenView(name: name, data: StringAnyEncodable(data))
guard let logger = diGraph?.logger else {
return
}
automaticScreenView(name: name, data: StringAnyEncodable(logger: logger, data))
}

// Designed to be called from swizzled methods for automatic screen tracking.
Expand Down Expand Up @@ -396,4 +399,4 @@ public class CustomerIO: CustomerIOInstance {
) {
implementation?.trackMetric(deliveryID: deliveryID, event: event, deviceToken: deviceToken)
}
}
} // swiftlint:disable:this file_length
9 changes: 4 additions & 5 deletions Sources/Tracking/CustomerIOImplementation.swift
Expand Up @@ -157,7 +157,7 @@ internal class CustomerIOImplementation: CustomerIOInstance {
}

public func identify(identifier: String, body: [String: Any]) {
identify(identifier: identifier, body: StringAnyEncodable(body))
identify(identifier: identifier, body: StringAnyEncodable(logger: logger, body))
}

public func clearIdentify() {
Expand Down Expand Up @@ -191,11 +191,11 @@ internal class CustomerIOImplementation: CustomerIOInstance {
}

public func track(name: String, data: [String: Any]) {
track(name: name, data: StringAnyEncodable(data))
track(name: name, data: StringAnyEncodable(logger: logger, data))
}

public func screen(name: String, data: [String: Any]) {
screen(name: name, data: StringAnyEncodable(data))
screen(name: name, data: StringAnyEncodable(logger: logger, data))
}

public func screen<RequestBody: Encodable>(
Expand Down Expand Up @@ -251,8 +251,7 @@ internal class CustomerIOImplementation: CustomerIOInstance {
deviceAttributesProvider.getDefaultDeviceAttributes { defaultDeviceAttributes in
let deviceAttributes = defaultDeviceAttributes.mergeWith(customAttributes)

let encodableBody =
StringAnyEncodable(deviceAttributes) // makes [String: Any] Encodable to use in JSON body.
let encodableBody = StringAnyEncodable(logger: self.logger, deviceAttributes) // makes [String: Any] Encodable to use in JSON body.
let requestBody = RegisterDeviceRequest(device: Device(
token: deviceToken,
platform: deviceOsName,
Expand Down
18 changes: 11 additions & 7 deletions Tests/Common/Util/StringAnyEncodable.swift
Expand Up @@ -9,13 +9,15 @@ struct DummyData: Codable, Equatable {
let testValue: String
let dict: [String: String]
let array: [Int]
let dictWithArray: [String: [String]]

enum CodingKeys: String, CodingKey {
case boolean
case numeric
case testValue
case dict
case array
case dictWithArray
}
}

Expand All @@ -29,7 +31,7 @@ class StringAnyEncodableTest: UnitTest {

let data = ["fooBar": Unencodable(data: 12345)] as [String: Any]

let json = StringAnyEncodable(data)
let json = StringAnyEncodable(logger: log, data)

guard let actual = jsonAdapter.toJson(json) else {
XCTFail("couldn't encode to JSON")
Expand All @@ -44,7 +46,7 @@ class StringAnyEncodableTest: UnitTest {

let data = ["fooBar": "bar"] as [String: String]

let json = StringAnyEncodable(data)
let json = StringAnyEncodable(logger: log, data)

guard let actual = jsonAdapter.toJson(json) else {
XCTFail("couldn't encode to JSON")
Expand All @@ -59,7 +61,7 @@ class StringAnyEncodableTest: UnitTest {

let data = ["fooBar": 1.2] as [String: Double]

let json = StringAnyEncodable(data)
let json = StringAnyEncodable(logger: log, data)

guard let actual = jsonAdapter.toJson(json) else {
XCTFail("couldn't encode to JSON")
Expand All @@ -74,7 +76,7 @@ class StringAnyEncodableTest: UnitTest {

let data = ["fooBar": ["bar": 1000] as [String: Int]] as [String: Any]

let json = StringAnyEncodable(data)
let json = StringAnyEncodable(logger: log, data)

guard let actual = jsonAdapter.toJson(json) else {
XCTFail("couldn't encode to JSON")
Expand All @@ -85,17 +87,19 @@ class StringAnyEncodableTest: UnitTest {
}

func test_stringanyencodable_encodes_complex_data() {
let expect = DummyData(boolean: true, numeric: 1, testValue: "foo", dict: ["test": "value"], array: [1, 2, 4])
let expect = DummyData(boolean: true, numeric: 1, testValue: "foo", dict: ["test": "value"], array: [1, 2, 4], dictWithArray: ["color": ["Red", "Green", "Blue"]])

// React native wrap some values in AnyHashable
let data = [
"testValue": "foo",
"numeric": 1,
"boolean": true,
"array": [1, 2, 4],
"dict": ["test": "value"] as [String: Any]
"dict": ["test": "value"] as [String: Any],
"dictWithArray": ["color": ["Red", "Green", "Blue"]] as [String: [Any]]
] as [String: Any]

let json = StringAnyEncodable(data)
let json = StringAnyEncodable(logger: log, data)

guard let actual = jsonAdapter.toJson(json) else {
XCTFail("couldn't encode to JSON")
Expand Down
2 changes: 1 addition & 1 deletion Tests/Tracking/CustomerIOImplementationTest.swift
Expand Up @@ -323,7 +323,7 @@ class CustomerIOImplementationTest: UnitTest {
platform: "iOS",
lastUsed: dateUtilStub
.givenNow,
attributes: StringAnyEncodable(givenDefaultAttributes)
attributes: StringAnyEncodable(logger: log, givenDefaultAttributes)
)
))
XCTAssertEqual(actualQueueTaskData?.attributesJsonString, expectedJsonString)
Expand Down

0 comments on commit a634358

Please sign in to comment.