Skip to content

Conversation

@BrandonStalnaker
Copy link
Collaborator

Summary

  • Port NSDictionary(MPCaseInsensitive) to Swift

Testing Plan

  • ran in sample app and confirmed all unit tests

Reference Issue (For mParticle employees only. Ignore if you are an outside contributor)

Copy link
Contributor

@einsteinx2 einsteinx2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that the MPKitContainerTests testForwardAppsFlyerEvent test is failing on this branch due to these changes. I checked out the development branch and confirmed it passes there.

Once that test is fixed and new dedicated tests added for these methods, I can re-review and make any final suggestions to simplify the implementation.


@objc func transformValuesToString() -> [String : Any] {
let transformedDictionary: [String : Any] = self.reduce(into: [:]) { result, element in
let key = element.key as! String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will crash any time element.key is not a String. Since this is inside a reduce closure, you can use a guard let statement here and it will return from the closure, not the whole function.

Something like this should work:

Suggested change
let key = element.key as! String
guard let key = element.key as? String else { return }


public func transformValuesToString() -> [String : Any] {
let transformedDictionary: [String : Any] = self.reduce(into: [:]) { result, element in
let key = element.key as! String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Suggested change
let key = element.key as! String
guard let key = element.key as? String else { return }

Comment on lines +12 to +19
var localKey = key

self.allKeys.forEach { keyValue in
if let stringKey = keyValue as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
localKey = stringKey
}
}
return localKey
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has slightly different behavior than the original Obj-C code. In the original, it stops searching after finding the correct key, but in this code it will continue. It's not possible to use break here as it it's an actual for loop, and if you use return it will return from the closure, but I believe it will continue to loop. Instead this can be written as a for in loop:

Suggested change
var localKey = key
self.allKeys.forEach { keyValue in
if let stringKey = keyValue as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
localKey = stringKey
}
}
return localKey
for aKey in allKeys {
if let stringKey = aKey as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
return stringKey
}
}
return key

Comment on lines +23 to +31
var value : Any?

self.allKeys.forEach { keyValue in
if let stringKey = keyValue as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
value = self[stringKey]
}
}

return value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Also caqn be slightly simplified since with a for in loop you can get the key and value directly:

Suggested change
var value : Any?
self.allKeys.forEach { keyValue in
if let stringKey = keyValue as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
value = self[stringKey]
}
}
return value
for (aKey, value) in self {
if let stringKey = aKey as? String, stringKey.caseInsensitiveCompare(key) == .orderedSame {
return value
}
}
return nil

return value
}

@objc func transformValuesToString() -> [String : Any] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method really needs a dedicate unit test written in Objective-C that passes in a dictionary of all of the supported types from all the ifs and confirms the output strings. First make sure it passes against the original Obj-C implementation then against this one.

}

@objc func transformValuesToString() -> [String : Any] {
let transformedDictionary: [String : Any] = self.reduce(into: [:]) { result, element in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check that the use of the reduce method here doesn't bridge the NSDictionary to a Swift dict and mess up some of the types... The added unit test should catch that though.

}
}

extension Dictionary {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the tests are fixed and new tests added, this can probably be simplified so that you only write the implementations for NSDictionary or Swift dictionary and then cast to the other type. That way you don't need to duplicate the implementations.

For sure it can be done for the caseInsensitiveKey and value(forCaseInsensitiveKey key: String) methods, but transformValuesToString may have issues due to bridging messing up the types, though it would probably still be fine if the Swift implementation cases to NSDictionary vs the other way around.

@BrandonStalnaker BrandonStalnaker requested a review from a team as a code owner August 4, 2025 14:42
@BrandonStalnaker BrandonStalnaker force-pushed the development branch 6 times, most recently from c0f6f8e to 475e5d4 Compare August 4, 2025 20:23
Base automatically changed from development to main October 1, 2025 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants