Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Update NSDictionary+CaseInsensitive to Swift #326

Open
wants to merge 1 commit into
base: development
Choose a base branch
from

Conversation

BrandonStalnaker
Copy link
Contributor

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.

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.

2 participants