diff --git a/Package.swift b/Package.swift index 1820ef6..2014074 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "mParticle-Rokt", - platforms: [ .iOS(.v11), .tvOS(.v11) ], + platforms: [ .iOS(.v15), .tvOS(.v15) ], products: [ .library( name: "mParticle-Rokt", diff --git a/mParticle-Rokt/MPKitRokt.h b/mParticle-Rokt/MPKitRokt.h index a646420..8e8351a 100644 --- a/mParticle-Rokt/MPKitRokt.h +++ b/mParticle-Rokt/MPKitRokt.h @@ -2,12 +2,8 @@ #if defined(__has_include) && __has_include() #import #import -#elif defined(__has_include) && __has_include() - #import - #import #else #import "mParticle.h" - #import "mParticle_Apple_SDK-Swift.h" #endif @interface MPKitRokt : NSObject diff --git a/mParticle-Rokt/MPKitRokt.m b/mParticle-Rokt/MPKitRokt.m index 6ea51e8..f93f7d1 100644 --- a/mParticle-Rokt/MPKitRokt.m +++ b/mParticle-Rokt/MPKitRokt.m @@ -1,12 +1,12 @@ #import "MPKitRokt.h" #import -NSString * const kMPRemoteConfigKitHashesKey = @"hs"; +NSString * const kMPRoktRemoteConfigKitHashesKey = @"hs"; NSString * const kMPRemoteConfigUserAttributeFilter = @"ua"; NSString * const MPKitRoktErrorDomain = @"com.mparticle.kits.rokt"; NSString * const MPKitRoktErrorMessageKey = @"mParticle-Rokt Error"; -NSString * const kMPPlacementAttributesMapping = @"placementAttributesMapping"; -NSString * const kMPHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; +NSString * const kMPRoktPlacementAttributesMapping = @"placementAttributesMapping"; +NSString * const kMPRoktHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; NSString * const kMPRoktEmbeddedViewClassName = @"MPRoktEmbeddedView"; NSInteger const kMPRoktKitCode = 181; @@ -55,17 +55,24 @@ - (MPKitExecStatus *)didFinishLaunchingWithConfiguration:(NSDictionary *)configu // Initialize Rokt SDK here [MPKitRokt MPLog:[NSString stringWithFormat:@"Attempting to initialize Rokt with Kit Version: %@", kitVersion]]; - [Rokt initWithRoktTagId:partnerId mParticleSdkVersion:sdkVersion mParticleKitVersion:kitVersion onInitComplete:^(BOOL InitComplete) { - if (InitComplete) { - [self start]; - [MPKitRokt MPLog:@"Rokt Init Complete"]; - NSDictionary *userInfo = @{mParticleKitInstanceKey:[[self class] kitCode]}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"mParticle.Rokt.Initialized" - object:nil - userInfo:userInfo]; + + // Subscribe to global events to receive InitComplete + [Rokt globalEventsOnEvent:^(RoktEvent * _Nonnull event) { + if ([event isKindOfClass:[InitComplete class]]) { + InitComplete *initComplete = (InitComplete *)event; + if (initComplete.success) { + [self start]; + [MPKitRokt MPLog:@"Rokt Init Complete"]; + NSDictionary *userInfo = @{mParticleKitInstanceKey:[[self class] kitCode]}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"mParticle.Rokt.Initialized" + object:nil + userInfo:userInfo]; + } } }]; + [Rokt initWithRoktTagId:partnerId mParticleSdkVersion:sdkVersion mParticleKitVersion:kitVersion]; + return [self execStatus:MPKitReturnCodeSuccess]; } @@ -91,7 +98,7 @@ - (void)start { /// /// \param embeddedViews A dictionary of RoktEmbeddedViews with their names /// -/// \param callbacks Object that contains all possible callbacks for selectPlacements +/// \param onEvent Callback block that receives RoktEvent objects for all placement events /// /// \param filteredUser The current user when this placement was requested. Filtered for the kit as per settings in the mParticle UI /// @@ -99,25 +106,27 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier attributes:(NSDictionary * _Nonnull)attributes embeddedViews:(NSDictionary * _Nullable)embeddedViews config:(MPRoktConfig * _Nullable)mpRoktConfig - callbacks:(MPRoktEventCallback * _Nullable)callbacks + onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser { - [MPKitRokt MPLog:[NSString stringWithFormat:@"Rokt Kit recieved `executeWithIdentifier` method with the following arguments: \n identifier: %@ \n attributes: %@ \n embeddedViews: %@ \n config: %@ \n callbacks: %@ \n filteredUser identities: %@", identifier, attributes, embeddedViews, mpRoktConfig, callbacks, filteredUser.userIdentities]]; + [MPKitRokt MPLog:[NSString stringWithFormat:@"Rokt Kit recieved `executeWithIdentifier` method with the following arguments: \n identifier: %@ \n attributes: %@ \n embeddedViews: %@ \n config: %@ \n onEvent: %@ \n filteredUser identities: %@", identifier, attributes, embeddedViews, mpRoktConfig, onEvent, filteredUser.userIdentities]]; NSDictionary *finalAtt = [MPKitRokt prepareAttributes:attributes filteredUser:filteredUser performMapping:NO]; //Convert MPRoktConfig to RoktConfig RoktConfig *roktConfig = [MPKitRokt convertMPRoktConfig:mpRoktConfig]; NSDictionary *confirmedViews = [self confirmEmbeddedViews:embeddedViews]; - [Rokt executeWithViewName:identifier - attributes:finalAtt - placements:confirmedViews - config:roktConfig - onLoad:callbacks.onLoad - onUnLoad:callbacks.onUnLoad - onShouldShowLoadingIndicator:callbacks.onShouldShowLoadingIndicator - onShouldHideLoadingIndicator:callbacks.onShouldHideLoadingIndicator - onEmbeddedSizeChange:callbacks.onEmbeddedSizeChange - ]; + [Rokt selectPlacementsWithIdentifier:identifier + attributes:finalAtt + placements:confirmedViews + config:roktConfig + onEvent:^(RoktEvent * _Nonnull event) { + if (onEvent) { + MPRoktEvent *mpEvent = [MPKitRokt mapEvent:event]; + if (mpEvent) { + onEvent(mpEvent); + } + } + }]; return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeSuccess]; } @@ -254,7 +263,7 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk transformedDictionary[key] = [numberAttribute stringValue]; } } else if ([obj isKindOfClass:[NSDate class]]) { - transformedDictionary[key] = [MPDateFormatter stringFromDateRFC3339:obj]; + transformedDictionary[key] = [MPKitAPI stringFromDateRFC3339:obj]; } else if ([obj isKindOfClass:[NSData class]] && [(NSData *)obj length] > 0) { transformedDictionary[key] = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding]; } else if ([obj isKindOfClass:[NSDictionary class]]) { @@ -295,8 +304,8 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk NSData *dataAttributeMap; // Rokt Kit is available though there may not be an attribute map attributeMap = @[]; - if (roktKitConfig[kMPPlacementAttributesMapping] != [NSNull null]) { - strAttributeMap = [roktKitConfig[kMPPlacementAttributesMapping] stringByRemovingPercentEncoding]; + if (roktKitConfig[kMPRoktPlacementAttributesMapping] != [NSNull null]) { + strAttributeMap = [roktKitConfig[kMPRoktPlacementAttributesMapping] stringByRemovingPercentEncoding]; dataAttributeMap = [strAttributeMap dataUsingEncoding:NSUTF8StringEncoding]; } @@ -475,25 +484,32 @@ + (NSNumber *)getRoktHashedEmailUserIdentityType { NSDictionary *roktKitConfig = [MPKitRokt getKitConfig]; // Get the string representing which identity to use and convert it to the key (NSNumber) - NSString *hashedIdentityTypeString = roktKitConfig[kMPHashedEmailUserIdentityType]; + NSString *hashedIdentityTypeString = roktKitConfig[kMPRoktHashedEmailUserIdentityType]; NSNumber *hashedIdentityTypeNumber = [MPKitRokt identityTypeForString:hashedIdentityTypeString.lowercaseString]; return hashedIdentityTypeNumber; } -- (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId catalogItemId:(NSString *)catalogItemId success:(NSNumber *)success { - if (placementId != nil && catalogItemId != nil && success != nil) { - if (@available(iOS 15.0, *)) { - [Rokt purchaseFinalizedWithPlacementId:placementId catalogItemId:catalogItemId success:success.boolValue]; - return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeSuccess]; - } - return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeUnavailable]; +- (MPKitExecStatus *)purchaseFinalized:(NSString *)identifier catalogItemId:(NSString *)catalogItemId success:(NSNumber *)success { + if (identifier != nil && catalogItemId != nil && success != nil) { + [Rokt purchaseFinalizedWithIdentifier:identifier catalogItemId:catalogItemId success:success.boolValue]; + return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeSuccess]; } return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeFail]; } - (MPKitExecStatus *)events:(NSString *)identifier onEvent:(void (^)(MPRoktEvent * _Nonnull))onEvent { - [Rokt eventsWithViewName:identifier onEvent:^(RoktEvent * _Nonnull event) { + [Rokt eventsWithIdentifier:identifier onEvent:^(RoktEvent * _Nonnull event) { + MPRoktEvent *mpEvent = [MPKitRokt mapEvent:event]; + if (mpEvent) { + onEvent(mpEvent); + } + }]; + return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeSuccess]; +} + +- (MPKitExecStatus *)globalEvents:(void (^)(MPRoktEvent * _Nonnull))onEvent { + [Rokt globalEventsOnEvent:^(RoktEvent * _Nonnull event) { MPRoktEvent *mpEvent = [MPKitRokt mapEvent:event]; if (mpEvent) { onEvent(mpEvent); @@ -863,6 +879,13 @@ + (MPRoktEvent * _Nullable)mapEvent:(RoktEvent *)event { return [[MPRoktFirstPositiveEngagement alloc] initWithPlacementId:firstPositiveEngagement.placementId]; } + // Check for RoktEvent.EmbeddedSizeChanged + if ([event isKindOfClass:[EmbeddedSizeChanged class]]) { + EmbeddedSizeChanged *embeddedSizeChanged = (EmbeddedSizeChanged *)event; + return [[MPRoktEmbeddedSizeChanged alloc] initWithPlacementId:embeddedSizeChanged.placementId + updatedHeight:embeddedSizeChanged.updatedHeight]; + } + // Check for RoktEvent.CartItemInstantPurchase if ([event isKindOfClass:[CartItemInstantPurchase class]]) { CartItemInstantPurchase *cartItemInstantPurchase = (CartItemInstantPurchase *)event; diff --git a/mParticle_RoktTests/mParticle_RoktTests.m b/mParticle_RoktTests/mParticle_RoktTests.m index c3c7d88..f3d45a0 100644 --- a/mParticle_RoktTests/mParticle_RoktTests.m +++ b/mParticle_RoktTests/mParticle_RoktTests.m @@ -1,11 +1,11 @@ #import #import -#import +@import Rokt_Widget; #import #import "MPKitRokt.h" NSInteger const kMPRoktKitCode = 181; -NSString * const kMPHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; +NSString * const kMPRoktHashedEmailUserIdentityType = @"hashedEmailUserIdentityType"; @interface MPKitRokt () @@ -13,11 +13,11 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier attributes:(NSDictionary * _Nonnull)attributes embeddedViews:(NSDictionary * _Nullable)embeddedViews config:(MPRoktConfig * _Nullable)mpRoktConfig - callbacks:(MPRoktEventCallback * _Nullable)callbacks + onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser; - (MPKitExecStatus *)setWrapperSdk:(MPWrapperSdk)wrapperSdk version:(nonnull NSString *)wrapperSdkVersion; -- (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId +- (MPKitExecStatus *)purchaseFinalized:(NSString *)identifier catalogItemId:(NSString *)catalogItemId success:(NSNumber *)success; @@ -148,22 +148,18 @@ - (void)testExecuteWithIdentifier { @"sandbox": @"false" }; - // Expect Rokt execute call with correct parameters - OCMExpect([mockRoktSDK executeWithViewName:identifier - attributes:expectedAttributes - placements:OCMOCK_ANY - config:nil - onLoad:nil - onUnLoad:nil - onShouldShowLoadingIndicator:nil - onShouldHideLoadingIndicator:nil - onEmbeddedSizeChange:nil]); + // Expect Rokt selectPlacements call with correct parameters + OCMExpect([mockRoktSDK selectPlacementsWithIdentifier:identifier + attributes:expectedAttributes + placements:OCMOCK_ANY + config:nil + onEvent:OCMOCK_ANY]); MPKitExecStatus *status = [self.kitInstance executeWithIdentifier:identifier attributes:attributes embeddedViews:embeddedViews config:nil - callbacks:nil + onEvent:nil filteredUser:user]; // Verify @@ -186,22 +182,18 @@ - (void)testExecuteSandboxDetection { @"sandbox": @"true" }; - // Expect Rokt execute call with correct parameters - OCMExpect([mockRoktSDK executeWithViewName:identifier - attributes:expectedAttributes - placements:OCMOCK_ANY - config:nil - onLoad:nil - onUnLoad:nil - onShouldShowLoadingIndicator:nil - onShouldHideLoadingIndicator:nil - onEmbeddedSizeChange:nil]); + // Expect Rokt selectPlacements call with correct parameters + OCMExpect([mockRoktSDK selectPlacementsWithIdentifier:identifier + attributes:expectedAttributes + placements:OCMOCK_ANY + config:nil + onEvent:OCMOCK_ANY]); MPKitExecStatus *status = [self.kitInstance executeWithIdentifier:identifier attributes:attributes embeddedViews:embeddedViews config:nil - callbacks:nil + onEvent:nil filteredUser:user]; // Verify @@ -507,28 +499,26 @@ - (void)testSetWrapperSdk { } - (void)testPurchaseFinalized { - if (@available(iOS 15.0, *)) { - id mockRoktSDK = OCMClassMock([Rokt class]); - - // Set up test parameters - NSString *placementId = @"testonversion"; - NSString *catalogItemId = @"testcatalogItemId"; - BOOL success = YES; - - // Expect Rokt reportConversion call with correct parameters - OCMExpect([mockRoktSDK purchaseFinalizedWithPlacementId:placementId - catalogItemId:catalogItemId - success:success]); - - MPKitExecStatus *status = [self.kitInstance purchaseFinalized:placementId - catalogItemId:catalogItemId - success:@(success)]; - - // Verify - XCTAssertNotNil(status); - XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess); - OCMVerifyAll(mockRoktSDK); - } + id mockRoktSDK = OCMClassMock([Rokt class]); + + // Set up test parameters + NSString *identifier = @"testonversion"; + NSString *catalogItemId = @"testcatalogItemId"; + BOOL success = YES; + + // Expect Rokt purchaseFinalized call with correct parameters + OCMExpect([mockRoktSDK purchaseFinalizedWithIdentifier:identifier + catalogItemId:catalogItemId + success:success]); + + MPKitExecStatus *status = [self.kitInstance purchaseFinalized:identifier + catalogItemId:catalogItemId + success:@(success)]; + + // Verify + XCTAssertNotNil(status); + XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess); + OCMVerifyAll(mockRoktSDK); } - (void)testEvents_Success { @@ -539,7 +529,7 @@ - (void)testEvents_Success { __block MPRoktEvent *receivedEvent = nil; // Mock the Rokt SDK call and simulate triggering the callback with a mock event - OCMStub([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + OCMStub([mockRoktSDK eventsWithIdentifier:identifier onEvent:[OCMArg any]]).andDo(^(NSInvocation *invocation) { // Get the callback block from the invocation void (^onEventCallback)(RoktEvent *) = nil; [invocation getArgument:&onEventCallback atIndex:3]; // Index 3 is the second parameter (onEvent) @@ -560,7 +550,7 @@ - (void)testEvents_Success { }]; // Verify the Rokt SDK method was called - OCMVerify([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]); + OCMVerify([mockRoktSDK eventsWithIdentifier:identifier onEvent:[OCMArg any]]); // Verify the return status XCTAssertNotNil(status); @@ -582,7 +572,7 @@ - (void)testEvents_MappingReturnsNil { __block BOOL callbackCalled = NO; // Mock the Rokt SDK call and simulate triggering the callback with a mock event - OCMStub([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + OCMStub([mockRoktSDK eventsWithIdentifier:identifier onEvent:[OCMArg any]]).andDo(^(NSInvocation *invocation) { // Get the callback block from the invocation void (^onEventCallback)(RoktEvent *) = nil; [invocation getArgument:&onEventCallback atIndex:3]; @@ -602,7 +592,7 @@ - (void)testEvents_MappingReturnsNil { }]; // Verify the Rokt SDK method was called - OCMVerify([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]); + OCMVerify([mockRoktSDK eventsWithIdentifier:identifier onEvent:[OCMArg any]]); // Verify the return status XCTAssertNotNil(status); @@ -621,7 +611,7 @@ - (void)testEvents_NilIdentifier { __block BOOL callbackCalled = NO; // The Rokt SDK should still be called even with nil identifier - OCMExpect([mockRoktSDK eventsWithViewName:@"" onEvent:[OCMArg any]]); + OCMExpect([mockRoktSDK eventsWithIdentifier:@"" onEvent:[OCMArg any]]); // Execute the method under test MPKitExecStatus *status = [self.kitInstance events:identifier onEvent:^(MPRoktEvent * _Nonnull event) { @@ -629,7 +619,7 @@ - (void)testEvents_NilIdentifier { }]; // Verify the Rokt SDK method was called - OCMVerify([mockRoktSDK eventsWithViewName:@"" onEvent:[OCMArg any]]); + OCMVerify([mockRoktSDK eventsWithIdentifier:@"" onEvent:[OCMArg any]]); // Verify the return status XCTAssertNotNil(status); @@ -686,7 +676,7 @@ - (void)testGetRoktHashedEmailUserIdentityTypeOther4 { // Test case 1: When kit configuration exists with hashed email identity type NSDictionary *roktKitConfig = @{ @"id": @(kMPRoktKitCode), - kMPHashedEmailUserIdentityType: @"other4" + kMPRoktHashedEmailUserIdentityType: @"other4" }; // Mock the MParticle shared instance and kit container