11#import " MPKitRokt.h"
22#import < Rokt_Widget/Rokt_Widget-Swift.h>
33
4+ // Constants for kit configuration keys
5+ static NSString * const kMPKitConfigurationIdKey = @" id" ;
6+ static NSString * const kMPRemoteConfigKitConfigurationKey = @" as" ;
7+ static NSString * const kMPAttributeMappingSourceKey = @" map" ;
8+ static NSString * const kMPAttributeMappingDestinationKey = @" value" ;
9+
10+ // Rokt attribute keys
11+ static NSString * const kMPRoktAttributeKeySandbox = @" sandbox" ;
12+
13+ // Rokt kit constants
414NSString * const kMPRemoteConfigKitHashesKey = @" hs" ;
515NSString * const kMPRemoteConfigUserAttributeFilter = @" ua" ;
616NSString * const MPKitRoktErrorDomain = @" com.mparticle.kits.rokt" ;
919NSString * const kMPHashedEmailUserIdentityType = @" hashedEmailUserIdentityType" ;
1020NSString * const kMPRoktEmbeddedViewClassName = @" MPRoktEmbeddedView" ;
1121NSString * const kMPEventNameSelectPlacements = @" selectPlacements" ;
22+ NSString * const kMPRoktIdentityTypeEmailSha256 = @" emailsha256" ;
23+ NSString * const kMPRoktIdentityTypeMpid = @" mpid" ;
24+
25+ // Rokt kit identifier
1226NSInteger const kMPRoktKitCode = 181 ;
1327
1428static __weak MPKitRokt *roktKit = nil ;
@@ -86,16 +100,16 @@ - (void)start {
86100 });
87101}
88102
89- // / \param identifier The name that should be displayed in the widget
90- // /
91- // / \param attributes A string dictionary containing the parameters that should be displayed in the widget
92- // /
93- // / \ param embeddedViews A dictionary of RoktEmbeddedViews with their names
94- // /
95- // / \ param callbacks Object that contains all possible callbacks for selectPlacements
96- // /
97- // / \ param filteredUser The current user when this placement was requested. Filtered for the kit as per settings in the mParticle UI
98- // /
103+ // / Displays a Rokt ad placement with full configuration options.
104+ // / This method handles user identity synchronization, attribute mapping, and forwards the request to the Rokt SDK.
105+ // / Device identifiers (IDFA/IDFV) are automatically added if available.
106+ // / @param identifier The Rokt placement identifier configured in the Rokt dashboard
107+ // / @ param attributes Dictionary of user attributes (email, firstName, etc.). Attributes will be mapped according to dashboard configuration.
108+ // / @param embeddedViews Optional dictionary mapping placement identifiers to embedded view containers for inline placements
109+ // / @ param mpRoktConfig Optional Rokt configuration object (e.g., for dark mode or custom styling)
110+ // / @param callbacks Object that contains all possible callbacks for selectPlacements
111+ // / @ param filteredUser The current user when this placement was requested. Filtered for the kit as per settings in the mParticle UI
112+ // / @return MPKitExecStatus indicating success or failure of the operation
99113- (MPKitExecStatus *)executeWithIdentifier : (NSString * _Nullable)identifier
100114 attributes : (NSDictionary <NSString *, NSString *> * _Nonnull)attributes
101115 embeddedViews : (NSDictionary <NSString *, MPRoktEmbeddedView *> * _Nullable)embeddedViews
@@ -188,42 +202,41 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
188202 return safePlacements;
189203}
190204
205+ // / Ensures the "sandbox" attribute is present in the attributes dictionary.
206+ // / If not already set by the caller, the sandbox value is automatically determined based on the current mParticle environment
207+ // / (MPEnvironmentDevelopment → "true", production → "false"). This tells Rokt whether to show test or production ads.
208+ // / @param attributes The input attributes dictionary to validate
209+ // / @return A dictionary with the sandbox attribute guaranteed to be present
191210+ (NSDictionary <NSString *, NSString *> *)confirmSandboxAttribute : (NSDictionary <NSString *, NSString *> * _Nullable)attributes {
192211 NSMutableDictionary <NSString *, NSString *> *finalAttributes = attributes.mutableCopy ;
193- NSString *sandboxKey = @" sandbox" ;
194212
195213 // Determine the value of the sandbox attribute based off the current environment
196214 NSString *sandboxValue = ([[MParticle sharedInstance ] environment ] == MPEnvironmentDevelopment) ? @" true" : @" false" ;
197215
198216 if (finalAttributes != nil ) {
199- // Only set sandbox if it` s not set by the client
200- if (![finalAttributes.allKeys containsObject: sandboxKey ]) {
201- finalAttributes[sandboxKey ] = sandboxValue;
217+ // Only set sandbox if it' s not set by the client
218+ if (![finalAttributes.allKeys containsObject: kMPRoktAttributeKeySandbox ]) {
219+ finalAttributes[kMPRoktAttributeKeySandbox ] = sandboxValue;
202220 }
203221 } else {
204- finalAttributes = [[NSMutableDictionary alloc ] initWithDictionary: @{sandboxKey : sandboxValue}];
222+ finalAttributes = [[NSMutableDictionary alloc ] initWithDictionary: @{kMPRoktAttributeKeySandbox : sandboxValue}];
205223 }
206224
207- [MPKitRokt MPLog: [NSString stringWithFormat: @" Sandbox value: %@ " , finalAttributes[sandboxKey ]]];
225+ [MPKitRokt MPLog: [NSString stringWithFormat: @" Sandbox value: %@ " , finalAttributes[kMPRoktAttributeKeySandbox ]]];
208226 return finalAttributes;
209227}
210228
211229+ (NSDictionary <NSString *, NSString *> * _Nonnull)prepareAttributes : (NSDictionary <NSString *, NSString *> * _Nonnull)attributes filteredUser : (FilteredMParticleUser * _Nullable)filteredUser performMapping : (BOOL )performMapping {
212230 if (filteredUser == nil && roktKit != nil ) {
213231 filteredUser = [[[MPKitAPI alloc ] init ] getCurrentUserWithKit: roktKit];
214232 }
215- NSDictionary <NSString *, NSString *> *mpAttributes = [ self transformValuesToString: filteredUser.userAttributes] ;
233+ NSDictionary <NSString *, NSString *> *mappedAttributes = attributes ;
216234 if (performMapping) {
217- mpAttributes = [self mapAttributes: attributes filteredUser: filteredUser];
235+ mappedAttributes = [MPKitRokt mapAttributes: attributes filteredUser: filteredUser];
218236 }
219237
220238 NSMutableDictionary <NSString *, NSString *> *finalAtt = [[NSMutableDictionary alloc ] init ];
221- [finalAtt addEntriesFromDictionary: mpAttributes];
222-
223- // Add MPID to the attributes being passed to the Rokt SDK
224- if (filteredUser.userId .stringValue != nil ) {
225- [finalAtt addEntriesFromDictionary: @{@" mpid" : filteredUser.userId .stringValue }];
226- }
239+ [finalAtt addEntriesFromDictionary: mappedAttributes];
227240
228241 // Add all known user identities to the attributes being passed to the Rokt SDK
229242 [self addIdentityAttributes: finalAtt filteredUser: filteredUser];
@@ -232,13 +245,12 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
232245 [self handleHashedEmail: finalAtt];
233246
234247 // The core SDK does not set sandbox on the user, but we must pass it to Rokt if provided
235- NSString *sandboxKey = @" sandbox" ;
236- if (attributes[sandboxKey] != nil ) {
237- [finalAtt addEntriesFromDictionary: @{sandboxKey: attributes[sandboxKey]}];
248+ if (attributes[kMPRoktAttributeKeySandbox ] != nil ) {
249+ [finalAtt addEntriesFromDictionary: @{kMPRoktAttributeKeySandbox : attributes[kMPRoktAttributeKeySandbox ]}];
238250 }
239251
240252 [MPKitRokt MPLog: [NSString stringWithFormat: @" Attributes updated with mapped user Attributes and Identities: %@ " , finalAtt]];
241- return [self confirmSandboxAttribute: finalAtt];
253+ return [MPKitRokt confirmSandboxAttribute: finalAtt];
242254}
243255
244256+ (NSDictionary <NSString *, NSString *> *)transformValuesToString : (NSDictionary <NSString *, id> * _Nullable)originalDictionary {
@@ -277,19 +289,18 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
277289 return transformedDictionary;
278290}
279291
292+ // / Retrieves the attribute mapping configuration for the Rokt Kit from the mParticle dashboard settings.
293+ // / The mapping defines how attribute keys should be renamed before being sent to Rokt (e.g., "userEmail" → "email").
294+ // / @param attributes The input attributes dictionary
295+ // / @param filteredUser The current mParticle user
296+ // / @return A dictionary with mapped attributes according to dashboard configuration
280297+ (NSDictionary <NSString *, NSString *> *)mapAttributes : (NSDictionary <NSString *, NSString *> * _Nullable)attributes filteredUser : (FilteredMParticleUser * _Nonnull)filteredUser {
281298 NSArray <NSDictionary <NSString *, NSString *> *> *attributeMap = nil ;
282299
283300 // Get the kit configuration
284- NSArray <NSDictionary *> *kitConfigs = [MParticle sharedInstance ].kitContainer_PRIVATE .originalConfig .copy ;
285- NSDictionary *roktKitConfig;
286- for (NSDictionary *kitConfig in kitConfigs) {
287- if (kitConfig[@" id" ] != nil && [kitConfig[@" id" ] integerValue ] == kMPRoktKitCode ) {
288- roktKitConfig = kitConfig;
289- }
290- }
301+ NSDictionary *roktKitConfig = [MPKitRokt getKitConfig ];
291302
292- // Return nil if no Rokt Kit configuration found
303+ // Return original attributes if no Rokt Kit configuration found
293304 if (!roktKitConfig) {
294305 return attributes;
295306 }
@@ -299,8 +310,9 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
299310 NSData *dataAttributeMap;
300311 // Rokt Kit is available though there may not be an attribute map
301312 attributeMap = @[];
302- if (roktKitConfig[kMPPlacementAttributesMapping ] != [NSNull null ]) {
303- strAttributeMap = [roktKitConfig[kMPPlacementAttributesMapping ] stringByRemovingPercentEncoding ];
313+ id configJSONString = roktKitConfig[kMPRemoteConfigKitConfigurationKey ][kMPPlacementAttributesMapping ];
314+ if (configJSONString != nil && configJSONString != [NSNull null ]) {
315+ strAttributeMap = [configJSONString stringByRemovingPercentEncoding ];
304316 dataAttributeMap = [strAttributeMap dataUsingEncoding: NSUTF8StringEncoding];
305317 }
306318
@@ -311,28 +323,29 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
311323 @try {
312324 attributeMap = [NSJSONSerialization JSONObjectWithData: dataAttributeMap options: kNilOptions error: &error];
313325 } @catch (NSException *exception) {
326+ [MPKitRokt MPLog: [NSString stringWithFormat: @" Exception parsing placement attribute map: %@ " , exception]];
314327 }
315328
316329 if (attributeMap && !error) {
317- NSLog ( @" %@ " , attributeMap) ;
330+ [MPKitRokt MPLog: [ NSString stringWithFormat: @" Successfully parsed placement attribute map with %lu entries " , ( unsigned long )attributeMap.count]] ;
318331 } else {
319- NSLog ( @" %@ " , error) ;
332+ [MPKitRokt MPLog: [ NSString stringWithFormat: @" Failed to parse placement attribute map: %@ " , error]] ;
320333 }
321334 }
322335
323336 if (attributeMap) {
324337 NSMutableDictionary *mappedAttributes = attributes.mutableCopy ;
325338 for (NSDictionary <NSString *, NSString *> *map in attributeMap) {
326- NSString *mapFrom = map[@" map " ];
327- NSString *mapTo = map[@" value " ];
339+ NSString *mapFrom = map[kMPAttributeMappingSourceKey ];
340+ NSString *mapTo = map[kMPAttributeMappingDestinationKey ];
328341 if (mappedAttributes[mapFrom]) {
329342 NSString * value = mappedAttributes[mapFrom];
330343 [mappedAttributes removeObjectForKey: mapFrom];
331344 mappedAttributes[mapTo] = value;
332345 }
333346 }
334347 for (NSString *key in mappedAttributes) {
335- if (![key isEqual: @" sandbox " ]) {
348+ if (![key isEqual: kMPRoktAttributeKeySandbox ]) {
336349 [[MParticle sharedInstance ].identity.currentUser setUserAttribute: key value: mappedAttributes[key]];
337350 }
338351 }
@@ -344,7 +357,7 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk
344357 }
345358 }
346359
347- return [self transformValuesToString: mappedAttributes];
360+ return [MPKitRokt transformValuesToString: mappedAttributes];
348361 } else {
349362 return attributes;
350363 }
@@ -362,14 +375,17 @@ + (void)addIdentityAttributes:(NSMutableDictionary<NSString *, NSString *> * _Nu
362375 } else {
363376 attributes = identityAttributes;
364377 }
378+
379+ // Add MPID to the attributes being passed to the Rokt SDK
380+ attributes[kMPRoktIdentityTypeMpid ] = filteredUser.userId .stringValue ;
365381}
366382
367383+ (void )handleHashedEmail : (NSMutableDictionary <NSString *, NSString *> * _Nullable)attributes {
368384 NSString *emailKey = [MPKitRokt stringForIdentityType: MPIdentityEmail];
369- NSString *hashedEmailValue = attributes[@" emailsha256 " ];
385+ NSString *hashedEmailValue = attributes[kMPRoktIdentityTypeEmailSha256 ];
370386
371387 // Remove email if hashed value set
372- if (hashedEmailValue != nil ) {
388+ if (emailKey != kMPRoktIdentityTypeEmailSha256 && hashedEmailValue != nil ) {
373389 [attributes removeObjectForKey: emailKey];
374390 }
375391}
@@ -397,7 +413,7 @@ + (NSString *)stringForIdentityType:(MPIdentity)identityType {
397413 NSNumber *hashedEmailIdentity = [MPKitRokt getRoktHashedEmailUserIdentityType ];
398414
399415 if (hashedEmailIdentity && hashedEmailIdentity.unsignedIntValue == identityType) {
400- return @" emailsha256 " ;
416+ return kMPRoktIdentityTypeEmailSha256 ;
401417 }
402418
403419 NSDictionary <NSNumber *, NSString *> *identityStrings = @{@(MPIdentityCustomerId): @" customerid" ,
@@ -462,29 +478,39 @@ + (NSNumber *)identityTypeForString:(NSString *)identityString {
462478 return identityNumbers[identityString];
463479}
464480
465- + (NSDictionary *)getKitConfig {
466- // Get the kit configuration
481+ #pragma mark - Private Helper Methods
482+
483+ // / Retrieves the Rokt Kit configuration from the kit container.
484+ // / @return The Rokt Kit configuration dictionary, or nil if Rokt Kit is not configured.
485+ + (NSDictionary * _Nullable)getKitConfig {
467486 NSArray <NSDictionary *> *kitConfigs = [MParticle sharedInstance ].kitContainer_PRIVATE .originalConfig .copy ;
468- NSDictionary *roktKitConfig;
469487 for (NSDictionary *kitConfig in kitConfigs) {
470- if (kitConfig[ @" id " ] != nil && [ kitConfig[@" id " ] integerValue ] == kMPRoktKitCode ) {
471- roktKitConfig = kitConfig;
488+ if ([ kitConfig[kMPKitConfigurationIdKey ] integerValue ] == kMPRoktKitCode ) {
489+ return kitConfig;
472490 }
473491 }
474-
475- return roktKitConfig;
492+ return nil ;
476493}
477494
478- + (NSNumber *)getRoktHashedEmailUserIdentityType {
495+ // / Retrieves the configured identity type to use for hashed email from the Rokt Kit configuration.
496+ // / The hashed email identity type is determined by dashboard settings and may vary (e.g., CustomerId, Other, etc.).
497+ // / @return The NSNumber representing the MPIdentity type for hashed email, or nil if not configured.
498+ + (NSNumber * _Nullable)getRoktHashedEmailUserIdentityType {
479499 NSDictionary *roktKitConfig = [MPKitRokt getKitConfig ];
480500
481501 // Get the string representing which identity to use and convert it to the key (NSNumber)
482- NSString *hashedIdentityTypeString = roktKitConfig[kMPHashedEmailUserIdentityType ];
502+ NSString *hashedIdentityTypeString = roktKitConfig[kMPRemoteConfigKitConfigurationKey ][ kMPHashedEmailUserIdentityType ];
483503 NSNumber *hashedIdentityTypeNumber = [MPKitRokt identityTypeForString: hashedIdentityTypeString.lowercaseString];
484504
485505 return hashedIdentityTypeNumber;
486506}
487507
508+ // / Notifies Rokt that a purchase from a placement offer has been finalized.
509+ // / Call this method to inform Rokt about the completion status of an offer purchase initiated from a placement.
510+ // / @param placementId The identifier of the placement where the offer was displayed
511+ // / @param catalogItemId The identifier of the catalog item that was purchased
512+ // / @param success Whether the purchase was successful (YES) or failed (NO)
513+ // / @return MPKitExecStatus indicating success or failure of the operation
488514- (MPKitExecStatus *)purchaseFinalized : (NSString *)placementId catalogItemId : (NSString *)catalogItemId success : (NSNumber *)success {
489515 if (placementId != nil && catalogItemId != nil && success != nil ) {
490516 if (@available (iOS 15.0 , *)) {
0 commit comments