diff --git a/mParticle-Rokt.podspec b/mParticle-Rokt.podspec index d2d2fa4..f3e0243 100644 --- a/mParticle-Rokt.podspec +++ b/mParticle-Rokt.podspec @@ -15,7 +15,8 @@ Pod::Spec.new do |s| s.swift_version = '5.3' s.ios.deployment_target = "12.0" - s.ios.source_files = 'mParticle-Rokt/*.{h,m}' + s.ios.source_files = 'mParticle-Rokt/*.{h,m,swift}' + s.ios.public_header_files = 'mParticle-Rokt/*.h' s.ios.dependency 'mParticle-Apple-SDK', '~> 8.0' s.ios.dependency 'Rokt-Widget', '~> 4.10' end diff --git a/mParticle-Rokt.xcodeproj/project.pbxproj b/mParticle-Rokt.xcodeproj/project.pbxproj index 9c68568..5c090f7 100644 --- a/mParticle-Rokt.xcodeproj/project.pbxproj +++ b/mParticle-Rokt.xcodeproj/project.pbxproj @@ -8,10 +8,12 @@ /* Begin PBXBuildFile section */ 2502325C2D7A7BF3004794A2 /* Rokt-Widget in Frameworks */ = {isa = PBXBuildFile; productRef = 2502325B2D7A7BF3004794A2 /* Rokt-Widget */; }; - 536803952B7BAE11000A10BE /* mParticle-Apple-SDK in Frameworks */ = {isa = PBXBuildFile; productRef = 536803942B7BAE11000A10BE /* mParticle-Apple-SDK */; }; - 7E15B2092D9AE82000C1FF3E /* mParticle-Apple-SDK in Frameworks */ = {isa = PBXBuildFile; productRef = 7E15B2082D9AE82000C1FF3E /* mParticle-Apple-SDK */; }; 7E15B20B2D9AE82600C1FF3E /* Rokt-Widget in Frameworks */ = {isa = PBXBuildFile; productRef = 7E15B20A2D9AE82600C1FF3E /* Rokt-Widget */; }; + 7EDDAAB02E05A88E00D089CF /* mParticle-Apple-SDK in Frameworks */ = {isa = PBXBuildFile; productRef = 7EDDAAAF2E05A88E00D089CF /* mParticle-Apple-SDK */; }; + 7EDDAAB22E05A89B00D089CF /* mParticle-Apple-SDK in Frameworks */ = {isa = PBXBuildFile; productRef = 7EDDAAB12E05A89B00D089CF /* mParticle-Apple-SDK */; }; 7EE7F13E2DA95BEE006C5440 /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 7EE7F13D2DA95BEE006C5440 /* OCMock */; }; + B34CE55A2E04356F00712DE1 /* MPRoktEventMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D778512E02845700D887A4 /* MPRoktEventMapper.swift */; }; + B3D778532E02845700D887A4 /* MPRoktEventMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D778512E02845700D887A4 /* MPRoktEventMapper.swift */; }; DBB01A601DC1478A00A7B188 /* mParticle_Rokt.h in Headers */ = {isa = PBXBuildFile; fileRef = DBB01A5E1DC1478A00A7B188 /* mParticle_Rokt.h */; settings = {ATTRIBUTES = (Public, ); }; }; DBB01A681DC1480700A7B188 /* MPKitRokt.h in Headers */ = {isa = PBXBuildFile; fileRef = DBB01A661DC1480700A7B188 /* MPKitRokt.h */; }; DBB01A691DC1480700A7B188 /* MPKitRokt.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB01A671DC1480700A7B188 /* MPKitRokt.m */; }; @@ -30,6 +32,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + B3D778512E02845700D887A4 /* MPRoktEventMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPRoktEventMapper.swift; sourceTree = ""; }; DBB01A5B1DC1478A00A7B188 /* mParticle_Rokt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = mParticle_Rokt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DBB01A5E1DC1478A00A7B188 /* mParticle_Rokt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mParticle_Rokt.h; sourceTree = ""; }; DBB01A5F1DC1478A00A7B188 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -45,8 +48,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7EDDAAB02E05A88E00D089CF /* mParticle-Apple-SDK in Frameworks */, 2502325C2D7A7BF3004794A2 /* Rokt-Widget in Frameworks */, - 536803952B7BAE11000A10BE /* mParticle-Apple-SDK in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -54,9 +57,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7E15B2092D9AE82000C1FF3E /* mParticle-Apple-SDK in Frameworks */, FF0BB640217A84E800B0556C /* mParticle_Rokt.framework in Frameworks */, 7EE7F13E2DA95BEE006C5440 /* OCMock in Frameworks */, + 7EDDAAB22E05A89B00D089CF /* mParticle-Apple-SDK in Frameworks */, 7E15B20B2D9AE82600C1FF3E /* Rokt-Widget in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -86,6 +89,7 @@ DBB01A5D1DC1478A00A7B188 /* mParticle-Rokt */ = { isa = PBXGroup; children = ( + B3D778512E02845700D887A4 /* MPRoktEventMapper.swift */, DBB01A661DC1480700A7B188 /* MPKitRokt.h */, DBB01A671DC1480700A7B188 /* MPKitRokt.m */, DBB01A5E1DC1478A00A7B188 /* mParticle_Rokt.h */, @@ -140,8 +144,8 @@ ); name = "mParticle-Rokt"; packageProductDependencies = ( - 536803942B7BAE11000A10BE /* mParticle-Apple-SDK */, 2502325B2D7A7BF3004794A2 /* Rokt-Widget */, + 7EDDAAAF2E05A88E00D089CF /* mParticle-Apple-SDK */, ); productName = "mParticle-Rokt"; productReference = DBB01A5B1DC1478A00A7B188 /* mParticle_Rokt.framework */; @@ -176,10 +180,12 @@ TargetAttributes = { DBB01A5A1DC1478A00A7B188 = { CreatedOnToolsVersion = 8.0; + LastSwiftMigration = 1640; ProvisioningStyle = Automatic; }; FF0BB63A217A84E800B0556C = { CreatedOnToolsVersion = 10.0; + LastSwiftMigration = 1640; ProvisioningStyle = Automatic; }; }; @@ -194,9 +200,9 @@ ); mainGroup = DBB01A511DC1478A00A7B188; packageReferences = ( - 536803932B7BAE11000A10BE /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */, 2502325A2D7A7BF3004794A2 /* XCRemoteSwiftPackageReference "rokt-sdk-ios" */, 7EE7F13C2DA95BEE006C5440 /* XCRemoteSwiftPackageReference "ocmock" */, + 7EDDAAAE2E05A88E00D089CF /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */, ); productRefGroup = DBB01A5C1DC1478A00A7B188 /* Products */; projectDirPath = ""; @@ -230,6 +236,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B34CE55A2E04356F00712DE1 /* MPRoktEventMapper.swift in Sources */, DBB01A691DC1480700A7B188 /* MPKitRokt.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -238,6 +245,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B3D778532E02845700D887A4 /* MPRoktEventMapper.swift in Sources */, FF0BB63E217A84E800B0556C /* mParticle_RoktTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -278,6 +286,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -329,6 +338,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -352,6 +362,7 @@ DBB01A641DC1478A00A7B188 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -371,12 +382,15 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.mparticle.mParticle-Rokt"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Debug; }; DBB01A651DC1478A00A7B188 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -396,6 +410,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.mparticle.mParticle-Rokt"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -404,6 +419,7 @@ buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; @@ -433,6 +449,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.mparticle.mParticle-RoktTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -442,6 +460,7 @@ buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; @@ -470,6 +489,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.mparticle.mParticle-RoktTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -515,9 +535,9 @@ minimumVersion = 4.8.1; }; }; - 536803932B7BAE11000A10BE /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */ = { + 7EDDAAAE2E05A88E00D089CF /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mParticle/mparticle-apple-sdk"; + repositoryURL = "https://github.com/mParticle/mparticle-apple-sdk.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 8.4.0; @@ -539,20 +559,20 @@ package = 2502325A2D7A7BF3004794A2 /* XCRemoteSwiftPackageReference "rokt-sdk-ios" */; productName = "Rokt-Widget"; }; - 536803942B7BAE11000A10BE /* mParticle-Apple-SDK */ = { + 7E15B20A2D9AE82600C1FF3E /* Rokt-Widget */ = { isa = XCSwiftPackageProductDependency; - package = 536803932B7BAE11000A10BE /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */; - productName = "mParticle-Apple-SDK"; + package = 2502325A2D7A7BF3004794A2 /* XCRemoteSwiftPackageReference "rokt-sdk-ios" */; + productName = "Rokt-Widget"; }; - 7E15B2082D9AE82000C1FF3E /* mParticle-Apple-SDK */ = { + 7EDDAAAF2E05A88E00D089CF /* mParticle-Apple-SDK */ = { isa = XCSwiftPackageProductDependency; - package = 536803932B7BAE11000A10BE /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */; + package = 7EDDAAAE2E05A88E00D089CF /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */; productName = "mParticle-Apple-SDK"; }; - 7E15B20A2D9AE82600C1FF3E /* Rokt-Widget */ = { + 7EDDAAB12E05A89B00D089CF /* mParticle-Apple-SDK */ = { isa = XCSwiftPackageProductDependency; - package = 2502325A2D7A7BF3004794A2 /* XCRemoteSwiftPackageReference "rokt-sdk-ios" */; - productName = "Rokt-Widget"; + package = 7EDDAAAE2E05A88E00D089CF /* XCRemoteSwiftPackageReference "mparticle-apple-sdk" */; + productName = "mParticle-Apple-SDK"; }; 7EE7F13D2DA95BEE006C5440 /* OCMock */ = { isa = XCSwiftPackageProductDependency; diff --git a/mParticle-Rokt/MPKitRokt.m b/mParticle-Rokt/MPKitRokt.m index 04b954b..3b6711f 100644 --- a/mParticle-Rokt/MPKitRokt.m +++ b/mParticle-Rokt/MPKitRokt.m @@ -1,5 +1,6 @@ #import "MPKitRokt.h" #import +#import NSString * const kMPRemoteConfigKitHashesKey = @"hs"; NSString * const kMPRemoteConfigUserAttributeFilter = @"ua"; @@ -153,10 +154,10 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk - (NSDictionary * _Nullable) confirmEmbeddedViews:(NSDictionary * _Nullable)embeddedViews { NSMutableDictionary *safePlacements = [NSMutableDictionary dictionary]; - + for (NSString* key in embeddedViews) { MPRoktEmbeddedView *mpView = [embeddedViews objectForKey:key]; - + if ([mpView isKindOfClass:MPRoktEmbeddedView.class]) { // Create a new RoktEmbeddedView instance RoktEmbeddedView *roktView = [[RoktEmbeddedView alloc] initWithFrame:mpView.bounds]; @@ -166,7 +167,7 @@ - (RoktFrameworkType)mapMPWrapperSdkToRoktFrameworkType:(MPWrapperSdk)wrapperSdk [safePlacements setObject:roktView forKey:key]; } } - + return safePlacements; } @@ -296,6 +297,16 @@ - (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId catalogItemId:(NS 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) { + MPRoktEvent *mpEvent = [MPRoktEventMapper mapEvent:event]; + if (mpEvent) { + onEvent(mpEvent); + } + }]; + return [[MPKitExecStatus alloc] initWithSDKCode:[[self class] kitCode] returnCode:MPKitReturnCodeSuccess]; +} + #pragma mark - User attributes and identities - (MPKitExecStatus *)setUserIdentity:(NSString *)identityString identityType:(MPUserIdentity)identityType { diff --git a/mParticle-Rokt/MPRoktEventMapper.swift b/mParticle-Rokt/MPRoktEventMapper.swift new file mode 100644 index 0000000..c3e2e89 --- /dev/null +++ b/mParticle-Rokt/MPRoktEventMapper.swift @@ -0,0 +1,73 @@ +import Foundation +import mParticle_Apple_SDK +import Rokt_Widget + +/// Utility class for mapping Rokt events to mParticle events +@objc(MPRoktEventMapper) +@objcMembers +public class MPRoktEventMapper: NSObject { + + /// Maps a RoktEvent to the corresponding MPRoktEvent + /// - Parameter event: The RoktEvent to map + /// - Returns: The mapped MPRoktEvent, or nil if mapping fails + @objc(mapEvent:) + public static func mapEvent(_ event: RoktEvent) -> MPRoktEvent? { + switch event { + case let initComplete as RoktEvent.InitComplete: + return MPRoktEvent.MPRoktInitComplete(success: initComplete.success) + + case is RoktEvent.ShowLoadingIndicator: + return MPRoktEvent.MPRoktShowLoadingIndicator() + + case is RoktEvent.HideLoadingIndicator: + return MPRoktEvent.MPRoktHideLoadingIndicator() + + case let placementInteractive as RoktEvent.PlacementInteractive: + return MPRoktEvent.MPRoktPlacementInteractive(placementId: placementInteractive.placementId) + + case let placementReady as RoktEvent.PlacementReady: + return MPRoktEvent.MPRoktPlacementReady(placementId: placementReady.placementId) + + case let offerEngagement as RoktEvent.OfferEngagement: + return MPRoktEvent.MPRoktOfferEngagement(placementId: offerEngagement.placementId) + + case let openUrl as RoktEvent.OpenUrl: + return MPRoktEvent.MPRoktOpenUrl(placementId: openUrl.placementId, url: openUrl.url) + + case let positiveEngagement as RoktEvent.PositiveEngagement: + return MPRoktEvent.MPRoktPositiveEngagement(placementId: positiveEngagement.placementId) + + case let placementClosed as RoktEvent.PlacementClosed: + return MPRoktEvent.MPRoktPlacementClosed(placementId: placementClosed.placementId) + + case let placementCompleted as RoktEvent.PlacementCompleted: + return MPRoktEvent.MPRoktPlacementCompleted(placementId: placementCompleted.placementId) + + case let placementFailure as RoktEvent.PlacementFailure: + return MPRoktEvent.MPRoktPlacementFailure(placementId: placementFailure.placementId) + + case let firstPositiveEngagement as RoktEvent.FirstPositiveEngagement: + return MPRoktEvent.MPRoktFirstPositiveEngagement( + placementId: firstPositiveEngagement.placementId + ) + + case let cartItemInstantPurchase as RoktEvent.CartItemInstantPurchase: + return MPRoktEvent.MPRoktCartItemInstantPurchase( + placementId: cartItemInstantPurchase.placementId, + name: cartItemInstantPurchase.name ?? "", + cartItemId: cartItemInstantPurchase.cartItemId, + catalogItemId: cartItemInstantPurchase.catalogItemId, + currency: cartItemInstantPurchase.currency, + description: cartItemInstantPurchase.description, + linkedProductId: cartItemInstantPurchase.linkedProductId, + providerData: cartItemInstantPurchase.providerData, + quantity: cartItemInstantPurchase.quantity, + totalPrice: cartItemInstantPurchase.totalPrice, + unitPrice: cartItemInstantPurchase.unitPrice + ) + + default: + return nil + } + } +} diff --git a/mParticle-Rokt/mParticle_Rokt.h b/mParticle-Rokt/mParticle_Rokt.h index 3482555..a1fc808 100644 --- a/mParticle-Rokt/mParticle_Rokt.h +++ b/mParticle-Rokt/mParticle_Rokt.h @@ -2,9 +2,3 @@ FOUNDATION_EXPORT double mParticle_RoktVersionNumber; FOUNDATION_EXPORT const unsigned char mParticle_RoktVersionString[]; - -#if defined(__has_include) && __has_include() - #import -#else - #import "MPKitRokt.h" -#endif diff --git a/mParticle_RoktTests/mParticle_RoktTests.m b/mParticle_RoktTests/mParticle_RoktTests.m index 6596f57..b65683d 100644 --- a/mParticle_RoktTests/mParticle_RoktTests.m +++ b/mParticle_RoktTests/mParticle_RoktTests.m @@ -1,7 +1,9 @@ #import #import #import +#import #import "MPKitRokt.h" +#import @interface MPKitRokt () @@ -17,14 +19,14 @@ - (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId catalogItemId:(NSString *)catalogItemId success:(NSNumber *)success; -- (NSDictionary * _Nullable) confirmEmbeddedViews:(NSDictionary * _Nullable)embeddedViews; +- (NSDictionary * _Nullable) confirmEmbeddedViews:(NSDictionary * _Nullable)embeddedViews; - (NSDictionary *) filteredUserAttributes:(NSDictionary * _Nonnull)attributes kitConfiguration:(MPKitConfiguration *)kitConfiguration; - (void)addIdentityAttributes:(NSMutableDictionary * _Nullable)attributes filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser; + (RoktConfig *)convertMPRoktConfig:(MPRoktConfig *)mpRoktConfig; - + @end @interface mParticle_RoktTests : XCTestCase @@ -350,4 +352,111 @@ - (void)testPurchaseFinalized { } } -@end +- (void)testEvents_Success { + id mockRoktSDK = OCMClassMock([Rokt class]); + + NSString *identifier = @"TestViewName"; + __block BOOL callbackCalled = NO; + __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) { + // Get the callback block from the invocation + void (^onEventCallback)(RoktEvent *) = nil; + [invocation getArgument:&onEventCallback atIndex:3]; // Index 3 is the second parameter (onEvent) + + // Create a dummy ShowLoadingIndicator for testing + id mockRoktEvent = [[ShowLoadingIndicator alloc] init]; + + // Simulate the callback being called + if (onEventCallback) { + onEventCallback(mockRoktEvent); + } + }); + + // Execute the method under test + MPKitExecStatus *status = [self.kitInstance events:identifier onEvent:^(MPRoktEvent * _Nonnull event) { + callbackCalled = YES; + receivedEvent = event; + }]; + + // Verify the Rokt SDK method was called + OCMVerify([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]); + + // Verify the return status + XCTAssertNotNil(status); + XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess); + XCTAssertEqualObjects(status.integrationId, @181); + + // Verify the callback was called with the mapped event + XCTAssertTrue(callbackCalled); + XCTAssertNotNil(receivedEvent); + XCTAssertEqual([receivedEvent class], [MPRoktShowLoadingIndicator class]); + + [mockRoktSDK stopMocking]; +} + +- (void)testEvents_MappingReturnsNil { + id mockRoktSDK = OCMClassMock([Rokt class]); + + NSString *identifier = @"TestViewName"; + __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) { + // Get the callback block from the invocation + void (^onEventCallback)(RoktEvent *) = nil; + [invocation getArgument:&onEventCallback atIndex:3]; + + // Create a mock RoktEvent for testing + id mockRoktEvent = OCMClassMock([RoktEvent class]); + + // Simulate the callback being called + if (onEventCallback) { + onEventCallback(mockRoktEvent); + } + }); + + // Execute the method under test + MPKitExecStatus *status = [self.kitInstance events:identifier onEvent:^(MPRoktEvent * _Nonnull event) { + callbackCalled = YES; + }]; + + // Verify the Rokt SDK method was called + OCMVerify([mockRoktSDK eventsWithViewName:identifier onEvent:[OCMArg any]]); + + // Verify the return status + XCTAssertNotNil(status); + XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess); + + // Verify the callback was NOT called since mapping returned nil + XCTAssertFalse(callbackCalled); + + [mockRoktSDK stopMocking]; +} + +- (void)testEvents_NilIdentifier { + id mockRoktSDK = OCMClassMock([Rokt class]); + + NSString *identifier = @""; + __block BOOL callbackCalled = NO; + + // The Rokt SDK should still be called even with nil identifier + OCMExpect([mockRoktSDK eventsWithViewName:@"" onEvent:[OCMArg any]]); + + // Execute the method under test + MPKitExecStatus *status = [self.kitInstance events:identifier onEvent:^(MPRoktEvent * _Nonnull event) { + callbackCalled = YES; + }]; + + // Verify the Rokt SDK method was called + OCMVerify([mockRoktSDK eventsWithViewName:@"" onEvent:[OCMArg any]]); + + // Verify the return status + XCTAssertNotNil(status); + XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess); + + [mockRoktSDK stopMocking]; +} + +@end