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

Adds conversion of attribution to subscriber attributes #609

Merged
merged 11 commits into from Jul 21, 2021

Conversation

vegaro
Copy link
Contributor

@vegaro vegaro commented Jun 30, 2021

This was a tedious one because I had to basically mimic what the backend is doing to convert attribution data to subscriber attributes.

I removed the backend call, because it shouldn't be used anymore, and added conversion functions for each of the attribution networks.

Best way of reviewing this is by looking into the backend code specific for each network in the attribution_fields folder.

}

func testAdjustAttributionConversionWorksWithNullIDFA() {
let adjustData = adjustData(withIDFA: nil)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These tests are failing because I am inserting nils in the dictionary, which I am actually not sure it represents the real use of our addAttributionData function. I've looked at our swift interface and the data we accept in Purchases.addAttributionData is of type [AnyHashable: Any], but in these tests I assume developers can pass nil as values in the data. I don't think there's a way developers can do that right? @aboedo tagging you since you reviewed the Android counterpart and might know the answer.

Copy link
Member

@aboedo aboedo Jun 30, 2021

Choose a reason for hiding this comment

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

tricky one.

in objc, you can't set nil in a dict, but you can do NSNull.
in swift, you can't pass in something that's nil directly, as it won't technically be Any if it's nil.
So the following code:

Purchases.addAttributionData(["foo": nil], from: .adjust)

produces

'nil' is not compatible with expected dictionary value type 'Any'

buuuut
the following code:

let myDict: [String: Any?] = ["lalala": nil]
Purchases.addAttributionData(myDict, from: .adjust)

compiles just fine, because Any? counts as Any.
As you have probably guessed, using Any sucks. We should always strive to constrain the interface further than that to prevent this kind of issue.

I tried this out just to see what would happen with the old implementation, and it seems to get translated to NSNull.

Copy link
Member

Choose a reason for hiding this comment

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

and the NSNull eventually gets sent to the backend as the string "<null>" I believe. So in order to preserve this behavior we just need to make sure that we're still compatible with something like

Purchases.addAttributionData(["foo": NSNull()], from: .adjust)

@vegaro vegaro force-pushed the attribution-data-conversion branch 3 times, most recently from 3405cc1 to c4837cb Compare July 9, 2021 23:05
@vegaro vegaro force-pushed the attribution-data-conversion branch from c4837cb to 7c8ad4f Compare July 13, 2021 02:54
@vegaro vegaro marked this pull request as ready for review July 13, 2021 03:10
@vegaro vegaro requested a review from a team July 13, 2021 03:10
@vegaro vegaro mentioned this pull request Jul 13, 2021
4 tasks
Copy link
Member

@aboedo aboedo left a comment

Choose a reason for hiding this comment

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

looking great so far! left a few questions

Purchases/Attribution/RCAttributionFetcher.h Outdated Show resolved Hide resolved
Comment on lines +92 to +97
if let value = fixedData[AttributionKey.networkID.rawValue] {
convertedAttribution[SpecialSubscriberAttributes.appsFlyerID] = value
}
if let value = fixedData[AttributionKey.AppsFlyer.id.rawValue] {
convertedAttribution[SpecialSubscriberAttributes.appsFlyerID] = value
}
Copy link
Member

Choose a reason for hiding this comment

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

I'm not familiar with how the mapping should go for these, so I'm assuming that they're correct, and that these are mostly coming from reverse-engineering the values, right? there's no documentation we can point to?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I looked into khepri's code. I could point to that, but being this open source I am not sure it would make sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a comment to the new extracted function

Comment on lines +11 to +13
// swiftlint:disable identifier_name
@objc(RCSpecialSubscriberAttributes) public class SpecialSubscriberAttributes: NSObject {
@objc public static let email = "$email"
Copy link
Member

Choose a reason for hiding this comment

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

I'm guessing that this is done this way so we can share it with obj-c, and we'll probably update it during the swift migration, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes exactly

Purchases/Public/RCPurchases.m Outdated Show resolved Hide resolved
@@ -0,0 +1,162 @@
//
// RCAttributionFetcher.m
Copy link
Member

Choose a reason for hiding this comment

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

we need to update the name here

Purchases/Attribution/RCAttributionPoster.m Show resolved Hide resolved
@vegaro vegaro requested a review from aboedo July 21, 2021 13:17
Copy link
Member

@aboedo aboedo left a comment

Choose a reason for hiding this comment

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

looks great! I left one final naming change suggestion, ready to go after that

Comment on lines 722 to 730
private func function(keyPresence: KeyPresence, data: inout [String: Any], key: String, defaultValue: String) {
switch keyPresence {
case .defaultValue:
data[key] = defaultValue
case .nsNull:
data[key] = NSNull()
case .notPresent:
break
}
Copy link
Member

Choose a reason for hiding this comment

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

perhaps we can improve the naming for this func? updateMappingInData?
Also, super nitpicky but I'd usually have the inout param as the first item, so it's clear that that's the item being modified.
It'd also allow for naming like: updateMapping(inData: data, keyPresence: .nsNull, key: "idfa", defaultValue: "idfa")

Copy link
Contributor Author

@vegaro vegaro Jul 21, 2021

Choose a reason for hiding this comment

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

oh yes I forgot to update hahaha function lol 😆 Good catch!

@vegaro vegaro merged commit 42ac408 into main Jul 21, 2021
@vegaro vegaro deleted the attribution-data-conversion branch July 21, 2021 17:36
@vegaro vegaro added this to the 3.12.3 milestone Jul 21, 2021
@aboedo aboedo mentioned this pull request Aug 2, 2021
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.

None yet

2 participants