From e0b81981db3622112690358c85aacb89193c06f5 Mon Sep 17 00:00:00 2001 From: Brandon Stalnaker Date: Tue, 19 Aug 2025 15:30:59 -0400 Subject: [PATCH 1/2] feat: Map Selected Identity to Emailsha256 --- mParticle-Rokt-Swift/MPRoktLayout.swift | 8 ++- mParticle-Rokt/MPKitRokt.h | 1 + mParticle-Rokt/MPKitRokt.m | 83 ++++++++++++++++++++++- mParticle_RoktTests/mParticle_RoktTests.m | 68 +++++++++++++++++++ 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/mParticle-Rokt-Swift/MPRoktLayout.swift b/mParticle-Rokt-Swift/MPRoktLayout.swift index 3b8e51a..669e6b2 100644 --- a/mParticle-Rokt-Swift/MPRoktLayout.swift +++ b/mParticle-Rokt-Swift/MPRoktLayout.swift @@ -53,9 +53,10 @@ public class MPRoktLayout { } let email = attributes?["email"] let hashedEmail = attributes?["emailsha256"] + let hashedEmailIdentity = MPKitRokt.getHashedEmailUserIdentityType() let userEmailIdentity = user.identities[NSNumber(value: MPIdentity.email.rawValue)] - let userHashedEmailIdentity = user.identities[NSNumber(value: MPIdentity.other.rawValue)] + let userHashedEmailIdentity = user.identities[hashedEmailIdentity] let emailMismatch = email != nil && email != userEmailIdentity let hashedEmailMismatch = hashedEmail != nil && hashedEmail != userHashedEmailIdentity @@ -69,7 +70,7 @@ public class MPRoktLayout { print("The existing hashed email on the user (\(userHashedEmailIdentity ?? "nil")) does not match the email passed in to `selectPlacements:` (\(hashedEmail ?? "nil")). Please remember to sync the email identity to mParticle as soon as you receive it. We will now identify the user before creating the layout") } - syncIdentities(user: user, email: email, hashedEmail: hashedEmail, completion: completion) + syncIdentities(user: user, email: email, hashedEmail: hashedEmail, hashedEmailKey: hashedEmailIdentity, completion: completion) } else { completion() } @@ -79,11 +80,12 @@ public class MPRoktLayout { user: MParticleUser, email: String?, hashedEmail: String?, + hashedEmailKey: NSNumber, completion: @escaping () -> Void ) { let identityRequest = MPIdentityApiRequest(user: user) identityRequest.setIdentity(email, identityType: .email) - identityRequest.setIdentity(hashedEmail, identityType: .other) + identityRequest.setIdentity(hashedEmail, identityType: MPIdentity(rawValue: hashedEmailKey.uintValue) ?? .other) mparticle.identity.identify(identityRequest) {apiResult, error in if let error = error { diff --git a/mParticle-Rokt/MPKitRokt.h b/mParticle-Rokt/MPKitRokt.h index 1e96c8e..922e9a8 100644 --- a/mParticle-Rokt/MPKitRokt.h +++ b/mParticle-Rokt/MPKitRokt.h @@ -17,5 +17,6 @@ @property (nonatomic, unsafe_unretained, readonly) BOOL started; + (NSDictionary * _Nonnull)prepareAttributes:(NSDictionary * _Nonnull)attributes filteredUser:(FilteredMParticleUser * _Nullable)filteredUser performMapping:(BOOL)performMapping; ++ (NSNumber * _Nonnull)getRoktHashedEmailUserIdentityType; @end diff --git a/mParticle-Rokt/MPKitRokt.m b/mParticle-Rokt/MPKitRokt.m index 5e13430..11681b5 100644 --- a/mParticle-Rokt/MPKitRokt.m +++ b/mParticle-Rokt/MPKitRokt.m @@ -6,6 +6,7 @@ NSString * const MPKitRoktErrorDomain = @"com.mparticle.kits.rokt"; NSString * const MPKitRoktErrorMessageKey = @"mParticle-Rokt Error"; NSString * const kMPPlacementAttributesMapping = @"placementAttributesMapping"; +NSString * const kMPHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; static __weak MPKitRokt *roktKit = nil; @interface MPKitRokt () @@ -361,6 +362,12 @@ + (RoktConfig *)convertMPRoktConfig:(MPRoktConfig *)mpRoktConfig { } + (NSString *)stringForIdentityType:(MPIdentity)identityType { + NSNumber *hashedEmailIdentity = [MPKitRokt getRoktHashedEmailUserIdentityType]; + + if (hashedEmailIdentity.unsignedIntValue == identityType) { + return @"emailsha256"; + } + switch (identityType) { case MPIdentityCustomerId: return @"customerid"; @@ -381,8 +388,7 @@ + (NSString *)stringForIdentityType:(MPIdentity)identityType { return @"microsoft"; case MPIdentityOther: - // As of 7/30/2025, "MPIdentityOther" is used by Rokt customers to identify based off hashed email - return @"emailsha256"; + return @"other"; case MPIdentityTwitter: return @"twitter"; @@ -443,6 +449,79 @@ + (NSString *)stringForIdentityType:(MPIdentity)identityType { } } ++ (NSNumber *)identityTypeForString:(NSString *)identityString { + if ([identityString isEqualToString:@"customerid"]){ + return @(MPIdentityCustomerId); + } else if ([identityString isEqualToString:@"email"]){ + return @(MPIdentityEmail); + } else if ([identityString isEqualToString:@"facebook"]){ + return @(MPIdentityFacebook); + } else if ([identityString isEqualToString:@"facebookcustomaudienceid"]){ + return @(MPIdentityFacebookCustomAudienceId); + } else if ([identityString isEqualToString:@"google"]){ + return @(MPIdentityGoogle); + } else if ([identityString isEqualToString:@"microsoft"]){ + return @(MPIdentityMicrosoft); + } else if ([identityString isEqualToString:@"other"]){ + return @(MPIdentityOther); + } else if ([identityString isEqualToString:@"twitter"]){ + return @(MPIdentityTwitter); + } else if ([identityString isEqualToString:@"yahoo"]){ + return @(MPIdentityYahoo); + } else if ([identityString isEqualToString:@"other2"]){ + return @(MPIdentityOther2); + } else if ([identityString isEqualToString:@"other3"]){ + return @(MPIdentityOther3); + } else if ([identityString isEqualToString:@"other4"]){ + return @(MPIdentityOther4); + } else if ([identityString isEqualToString:@"other5"]){ + return @(MPIdentityOther5); + } else if ([identityString isEqualToString:@"other6"]){ + return @(MPIdentityOther6); + } else if ([identityString isEqualToString:@"other7"]){ + return @(MPIdentityOther7); + } else if ([identityString isEqualToString:@"other8"]){ + return @(MPIdentityOther8); + } else if ([identityString isEqualToString:@"other9"]){ + return @(MPIdentityOther9); + } else if ([identityString isEqualToString:@"other10"]){ + return @(MPIdentityOther10); + } else if ([identityString isEqualToString:@"mobile_number"]){ + return @(MPIdentityMobileNumber); + } else if ([identityString isEqualToString:@"phone_number_2"]){ + return @(MPIdentityPhoneNumber2); + } else if ([identityString isEqualToString:@"phone_number_3"]){ + return @(MPIdentityPhoneNumber3); + } else if ([identityString isEqualToString:@"ios_idfa"]){ + return @(MPIdentityIOSAdvertiserId); + } else if ([identityString isEqualToString:@"ios_idfv"]){ + return @(MPIdentityIOSVendorId); + } else if ([identityString isEqualToString:@"push_token"]){ + return @(MPIdentityPushToken); + } else if ([identityString isEqualToString:@"device_application_stamp"]){ + return @(MPIdentityDeviceApplicationStamp); + } else { + return nil; + } +} + ++ (NSNumber *)getRoktHashedEmailUserIdentityType { + // Get the kit configuration + NSArray *kitConfigs = [MParticle sharedInstance].kitContainer_PRIVATE.originalConfig.copy; + NSDictionary *roktKitConfig; + for (NSDictionary *kitConfig in kitConfigs) { + if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == 181) { + roktKitConfig = kitConfig; + } + } + + // Get the string representing which identity to use and convert it to the key (NSNumber) + NSString *hashedIdentityTypeString = roktKitConfig[kMPHashedEmailUserIdentityType]; + NSNumber *hashedIdentityTypeNumber = [MPKitRokt identityTypeForString:hashedIdentityTypeString.lowercaseString]; + + return hashedIdentityTypeNumber != nil ? hashedIdentityTypeNumber : @(MPIdentityOther); +} + - (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId catalogItemId:(NSString *)catalogItemId success:(NSNumber *)success { if (placementId != nil && catalogItemId != nil && success != nil) { if (@available(iOS 15.0, *)) { diff --git a/mParticle_RoktTests/mParticle_RoktTests.m b/mParticle_RoktTests/mParticle_RoktTests.m index 7638fb5..08dbef9 100644 --- a/mParticle_RoktTests/mParticle_RoktTests.m +++ b/mParticle_RoktTests/mParticle_RoktTests.m @@ -24,6 +24,8 @@ + (void)addIdentityAttributes:(NSMutableDictionary * _Nu + (void)handleHashedEmail:(NSMutableDictionary * _Nullable)attributes; ++ (NSNumber *)getRoktHashedEmailUserIdentityType; + + (RoktConfig *)convertMPRoktConfig:(MPRoktConfig *)mpRoktConfig; + (NSDictionary *)transformValuesToString:(NSDictionary * _Nullable)originalDictionary; @@ -329,6 +331,72 @@ - (void)testAddIdentityAttributesWithExistingAttributes { XCTAssertEqualObjects(passedAttributes[@"device_application_stamp"], @"Test DAS"); } +- (void)testAddIdentityAttributesWithExistingAttributesAndOther { + NSMutableDictionary *passedAttributes = [[NSMutableDictionary alloc] init]; + [passedAttributes setObject:@"bar" forKey:@"foo"]; + NSDictionary *testIdentities = @{@(MPIdentityCustomerId): @"testCustomerID", + @(MPIdentityEmail): @"testEmail@gmail.com", + @(MPIdentityFacebook): @"testFacebook", + @(MPIdentityFacebookCustomAudienceId): @"testCustomAudienceID", + @(MPIdentityGoogle): @"testGoogle", + @(MPIdentityMicrosoft): @"testMicrosoft", + @(MPIdentityOther): @"testOther", + @(MPIdentityTwitter): @"testTwitter", + @(MPIdentityYahoo): @"testYahoo", + @(MPIdentityOther2): @"testOther2", + @(MPIdentityOther3): @"testOther3", + @(MPIdentityOther4): @"testOther4", + @(MPIdentityOther5): @"testOther5", + @(MPIdentityOther6): @"testOther6", + @(MPIdentityOther7): @"testOther7", + @(MPIdentityOther8): @"testOther8", + @(MPIdentityOther9): @"testOther9", + @(MPIdentityOther10): @"testOther10", + @(MPIdentityMobileNumber): @"1(234)-567-8910", + @(MPIdentityPhoneNumber2): @"1(234)-567-2222", + @(MPIdentityPhoneNumber3): @"1(234)-567-3333", + @(MPIdentityIOSAdvertiserId): @"testAdvertID", + @(MPIdentityIOSVendorId): @"testVendorID", + @(MPIdentityPushToken): @"testPushToken", + @(MPIdentityDeviceApplicationStamp): @"Test DAS"}; + + FilteredMParticleUser *filteredUser = [[FilteredMParticleUser alloc] init]; + id mockfilteredUser = OCMPartialMock(filteredUser); + [[[mockfilteredUser stub] andReturn:testIdentities] userIdentities]; + id mockMPKitRoktClass = OCMClassMock([MPKitRokt class]); + [[[mockMPKitRoktClass stub] andReturn:@(MPIdentityOther4)] getRoktHashedEmailUserIdentityType]; + + [MPKitRokt addIdentityAttributes:passedAttributes filteredUser:filteredUser]; + + XCTAssertEqualObjects(passedAttributes[@"foo"], @"bar"); + XCTAssertEqualObjects(passedAttributes[@"customerid"], @"testCustomerID"); + XCTAssertEqualObjects(passedAttributes[@"email"], @"testEmail@gmail.com"); + XCTAssertEqualObjects(passedAttributes[@"facebook"], @"testFacebook"); + XCTAssertEqualObjects(passedAttributes[@"facebookcustomaudienceid"], @"testCustomAudienceID"); + XCTAssertEqualObjects(passedAttributes[@"google"], @"testGoogle"); + XCTAssertEqualObjects(passedAttributes[@"microsoft"], @"testMicrosoft"); + XCTAssertEqualObjects(passedAttributes[@"other"], @"testOther"); + XCTAssertEqualObjects(passedAttributes[@"emailsha256"], @"testOther4"); + XCTAssertEqualObjects(passedAttributes[@"twitter"], @"testTwitter"); + XCTAssertEqualObjects(passedAttributes[@"yahoo"], @"testYahoo"); + XCTAssertEqualObjects(passedAttributes[@"other2"], @"testOther2"); + XCTAssertEqualObjects(passedAttributes[@"other3"], @"testOther3"); + XCTAssertNil(passedAttributes[@"other4"]); + XCTAssertEqualObjects(passedAttributes[@"other5"], @"testOther5"); + XCTAssertEqualObjects(passedAttributes[@"other6"], @"testOther6"); + XCTAssertEqualObjects(passedAttributes[@"other7"], @"testOther7"); + XCTAssertEqualObjects(passedAttributes[@"other8"], @"testOther8"); + XCTAssertEqualObjects(passedAttributes[@"other9"], @"testOther9"); + XCTAssertEqualObjects(passedAttributes[@"other10"], @"testOther10"); + XCTAssertEqualObjects(passedAttributes[@"mobile_number"], @"1(234)-567-8910"); + XCTAssertEqualObjects(passedAttributes[@"phone_number_2"], @"1(234)-567-2222"); + XCTAssertEqualObjects(passedAttributes[@"phone_number_3"], @"1(234)-567-3333"); + XCTAssertEqualObjects(passedAttributes[@"ios_idfa"], @"testAdvertID"); + XCTAssertEqualObjects(passedAttributes[@"ios_idfv"], @"testVendorID"); + XCTAssertEqualObjects(passedAttributes[@"push_token"], @"testPushToken"); + XCTAssertEqualObjects(passedAttributes[@"device_application_stamp"], @"Test DAS"); +} + - (void)testConvertMPRoktConfig { MPRoktConfig *mpConfig = [[MPRoktConfig alloc] init]; mpConfig.colorMode = MPColorModeDark; From e811279b219d8f3ba53f1f1b8c006c9b86865b80 Mon Sep 17 00:00:00 2001 From: Brandon Stalnaker Date: Wed, 20 Aug 2025 13:54:02 -0400 Subject: [PATCH 2/2] addressed Denis comments --- mParticle-Rokt/MPKitRokt.m | 196 ++++++++++++------------------------- 1 file changed, 60 insertions(+), 136 deletions(-) diff --git a/mParticle-Rokt/MPKitRokt.m b/mParticle-Rokt/MPKitRokt.m index 11681b5..85502d0 100644 --- a/mParticle-Rokt/MPKitRokt.m +++ b/mParticle-Rokt/MPKitRokt.m @@ -7,6 +7,8 @@ NSString * const MPKitRoktErrorMessageKey = @"mParticle-Rokt Error"; NSString * const kMPPlacementAttributesMapping = @"placementAttributesMapping"; NSString * const kMPHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; +NSInteger const kMPRoktKitCode = 181; + static __weak MPKitRokt *roktKit = nil; @interface MPKitRokt () @@ -21,7 +23,7 @@ @implementation MPKitRokt mParticle will supply a unique kit code for you. Please contact our team */ + (NSNumber *)kitCode { - return @181; // Replace with the actual kit code assigned by mParticle + return @(kMPRoktKitCode); // Replace with the actual kit code assigned by mParticle } + (void)load { @@ -252,7 +254,7 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk NSArray *kitConfigs = [MParticle sharedInstance].kitContainer_PRIVATE.originalConfig.copy; NSDictionary *roktKitConfig; for (NSDictionary *kitConfig in kitConfigs) { - if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == 181) { + if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == kMPRoktKitCode) { roktKitConfig = kitConfig; } } @@ -367,142 +369,64 @@ + (NSString *)stringForIdentityType:(MPIdentity)identityType { if (hashedEmailIdentity.unsignedIntValue == identityType) { return @"emailsha256"; } - - switch (identityType) { - case MPIdentityCustomerId: - return @"customerid"; - - case MPIdentityEmail: - return @"email"; - - case MPIdentityFacebook: - return @"facebook"; - - case MPIdentityFacebookCustomAudienceId: - return @"facebookcustomaudienceid"; - - case MPIdentityGoogle: - return @"google"; - - case MPIdentityMicrosoft: - return @"microsoft"; - - case MPIdentityOther: - return @"other"; - - case MPIdentityTwitter: - return @"twitter"; - - case MPIdentityYahoo: - return @"yahoo"; - - case MPIdentityOther2: - return @"other2"; - - case MPIdentityOther3: - return @"other3"; - - case MPIdentityOther4: - return @"other4"; - - case MPIdentityOther5: - return @"other5"; - - case MPIdentityOther6: - return @"other6"; - - case MPIdentityOther7: - return @"other7"; - - case MPIdentityOther8: - return @"other8"; - - case MPIdentityOther9: - return @"other9"; - - case MPIdentityOther10: - return @"other10"; - - case MPIdentityMobileNumber: - return @"mobile_number"; - - case MPIdentityPhoneNumber2: - return @"phone_number_2"; - - case MPIdentityPhoneNumber3: - return @"phone_number_3"; - - case MPIdentityIOSAdvertiserId: - return @"ios_idfa"; - - case MPIdentityIOSVendorId: - return @"ios_idfv"; - - case MPIdentityPushToken: - return @"push_token"; - - case MPIdentityDeviceApplicationStamp: - return @"device_application_stamp"; - - default: - return nil; - } + + NSDictionary *identityStrings = @{@(MPIdentityCustomerId): @"customerid", + @(MPIdentityEmail): @"email", + @(MPIdentityFacebook): @"facebook", + @(MPIdentityFacebookCustomAudienceId): @"facebookcustomaudienceid", + @(MPIdentityGoogle): @"google", + @(MPIdentityMicrosoft): @"microsoft", + @(MPIdentityOther): @"other", + @(MPIdentityTwitter): @"twitter", + @(MPIdentityYahoo): @"yahoo", + @(MPIdentityOther2): @"other2", + @(MPIdentityOther3): @"other3", + @(MPIdentityOther4): @"other4", + @(MPIdentityOther5): @"other5", + @(MPIdentityOther6): @"other6", + @(MPIdentityOther7): @"other7", + @(MPIdentityOther8): @"other8", + @(MPIdentityOther9): @"other9", + @(MPIdentityOther10): @"other10", + @(MPIdentityMobileNumber): @"mobile_number", + @(MPIdentityPhoneNumber2): @"phone_number_2", + @(MPIdentityPhoneNumber3): @"phone_number_3", + @(MPIdentityIOSAdvertiserId): @"ios_idfa", + @(MPIdentityIOSVendorId): @"ios_idfv", + @(MPIdentityPushToken): @"push_token", + @(MPIdentityDeviceApplicationStamp): @"device_application_stamp"}; + + return identityStrings[@(identityType)]; } + (NSNumber *)identityTypeForString:(NSString *)identityString { - if ([identityString isEqualToString:@"customerid"]){ - return @(MPIdentityCustomerId); - } else if ([identityString isEqualToString:@"email"]){ - return @(MPIdentityEmail); - } else if ([identityString isEqualToString:@"facebook"]){ - return @(MPIdentityFacebook); - } else if ([identityString isEqualToString:@"facebookcustomaudienceid"]){ - return @(MPIdentityFacebookCustomAudienceId); - } else if ([identityString isEqualToString:@"google"]){ - return @(MPIdentityGoogle); - } else if ([identityString isEqualToString:@"microsoft"]){ - return @(MPIdentityMicrosoft); - } else if ([identityString isEqualToString:@"other"]){ - return @(MPIdentityOther); - } else if ([identityString isEqualToString:@"twitter"]){ - return @(MPIdentityTwitter); - } else if ([identityString isEqualToString:@"yahoo"]){ - return @(MPIdentityYahoo); - } else if ([identityString isEqualToString:@"other2"]){ - return @(MPIdentityOther2); - } else if ([identityString isEqualToString:@"other3"]){ - return @(MPIdentityOther3); - } else if ([identityString isEqualToString:@"other4"]){ - return @(MPIdentityOther4); - } else if ([identityString isEqualToString:@"other5"]){ - return @(MPIdentityOther5); - } else if ([identityString isEqualToString:@"other6"]){ - return @(MPIdentityOther6); - } else if ([identityString isEqualToString:@"other7"]){ - return @(MPIdentityOther7); - } else if ([identityString isEqualToString:@"other8"]){ - return @(MPIdentityOther8); - } else if ([identityString isEqualToString:@"other9"]){ - return @(MPIdentityOther9); - } else if ([identityString isEqualToString:@"other10"]){ - return @(MPIdentityOther10); - } else if ([identityString isEqualToString:@"mobile_number"]){ - return @(MPIdentityMobileNumber); - } else if ([identityString isEqualToString:@"phone_number_2"]){ - return @(MPIdentityPhoneNumber2); - } else if ([identityString isEqualToString:@"phone_number_3"]){ - return @(MPIdentityPhoneNumber3); - } else if ([identityString isEqualToString:@"ios_idfa"]){ - return @(MPIdentityIOSAdvertiserId); - } else if ([identityString isEqualToString:@"ios_idfv"]){ - return @(MPIdentityIOSVendorId); - } else if ([identityString isEqualToString:@"push_token"]){ - return @(MPIdentityPushToken); - } else if ([identityString isEqualToString:@"device_application_stamp"]){ - return @(MPIdentityDeviceApplicationStamp); - } else { - return nil; - } + NSDictionary *identityNumbers = @{@"customerid": @(MPIdentityCustomerId), + @"email": @(MPIdentityEmail), + @"facebook": @(MPIdentityFacebook), + @"facebookcustomaudienceid": @(MPIdentityFacebookCustomAudienceId), + @"google": @(MPIdentityGoogle), + @"microsoft": @(MPIdentityMicrosoft), + @"other": @(MPIdentityOther), + @"twitter": @(MPIdentityTwitter), + @"yahoo": @(MPIdentityYahoo), + @"other2": @(MPIdentityOther2), + @"other3": @(MPIdentityOther3), + @"other4": @(MPIdentityOther4), + @"other5": @(MPIdentityOther5), + @"other6": @(MPIdentityOther6), + @"other7": @(MPIdentityOther7), + @"other8": @(MPIdentityOther8), + @"other9": @(MPIdentityOther9), + @"other10": @(MPIdentityOther10), + @"mobile_number": @(MPIdentityMobileNumber), + @"phone_number_2": @(MPIdentityPhoneNumber2), + @"phone_number_3": @(MPIdentityPhoneNumber3), + @"ios_idfa": @(MPIdentityIOSAdvertiserId), + @"ios_idfv": @(MPIdentityIOSVendorId), + @"push_token": @(MPIdentityPushToken), + @"device_application_stamp": @(MPIdentityDeviceApplicationStamp)}; + + return identityNumbers[identityString]; } + (NSNumber *)getRoktHashedEmailUserIdentityType { @@ -510,7 +434,7 @@ + (NSNumber *)getRoktHashedEmailUserIdentityType { NSArray *kitConfigs = [MParticle sharedInstance].kitContainer_PRIVATE.originalConfig.copy; NSDictionary *roktKitConfig; for (NSDictionary *kitConfig in kitConfigs) { - if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == 181) { + if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == kMPRoktKitCode) { roktKitConfig = kitConfig; } }