diff --git a/MIGRATING.md b/MIGRATING.md index 51960e9a..dbe1590b 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -236,6 +236,191 @@ func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { --- +### MPRokt API Changes + +The `MPRokt` interface has been updated to align with the Rokt SDK 4.14.x API. These changes consolidate multiple callback parameters into a unified event-based callback pattern and standardize parameter naming. + +#### What Has Changed + +- The `MPRoktEventCallback` class has been removed and replaced with MPRoktEvent +- The `selectPlacements:` method's `callbacks:` parameter has been replaced with `onEvent:` +- The `purchaseFinalized:` method's `placementId:` parameter has been renamed to `identifier:` +- A new `globalEvents:` method has been added for subscribing to global Rokt events +- A new `MPRoktEmbeddedSizeChanged` event class has been added + +#### Migration Steps + +##### selectPlacements Method + +**Before (Objective-C):** + +```objective-c +MPRoktEventCallback *callbacks = [[MPRoktEventCallback alloc] init]; +callbacks.onLoad = ^{ + // Handle load +}; +callbacks.onUnLoad = ^{ + // Handle unload +}; +callbacks.onShouldShowLoadingIndicator = ^{ + // Show loading indicator +}; +callbacks.onShouldHideLoadingIndicator = ^{ + // Hide loading indicator +}; +callbacks.onEmbeddedSizeChange = ^(NSString *placementId, CGFloat height) { + // Handle size change +}; + +[[MParticle sharedInstance].rokt selectPlacements:@"checkout" + attributes:attributes + embeddedViews:embeddedViews + config:config + callbacks:callbacks]; +``` + +**After (Objective-C):** + +```objective-c +[[MParticle sharedInstance].rokt selectPlacements:@"checkout" + attributes:attributes + embeddedViews:embeddedViews + config:config + onEvent:^(MPRoktEvent * _Nonnull event) { + if ([event isKindOfClass:[MPRoktEvent.MPRoktShowLoadingIndicator class]]) { + // Show loading indicator + } else if ([event isKindOfClass:[MPRoktEvent.MPRoktHideLoadingIndicator class]]) { + // Hide loading indicator + } else if ([event isKindOfClass:[MPRoktEvent.MPRoktPlacementReady class]]) { + // Handle load/ready + } else if ([event isKindOfClass:[MPRoktEvent.MPRoktPlacementClosed class]]) { + // Handle unload/closed + } else if ([event isKindOfClass:[MPRoktEvent.MPRoktEmbeddedSizeChanged class]]) { + MPRoktEvent.MPRoktEmbeddedSizeChanged *sizeEvent = (MPRoktEvent.MPRoktEmbeddedSizeChanged *)event; + // Handle size change with sizeEvent.placementId and sizeEvent.updatedHeight + } +}]; +``` + +**Before (Swift):** + +```swift +let callbacks = MPRoktEventCallback() +callbacks.onLoad = { + // Handle load +} +callbacks.onUnLoad = { + // Handle unload +} +callbacks.onShouldShowLoadingIndicator = { + // Show loading indicator +} +callbacks.onShouldHideLoadingIndicator = { + // Hide loading indicator +} +callbacks.onEmbeddedSizeChange = { placementId, height in + // Handle size change +} + +MParticle.sharedInstance().rokt.selectPlacements("checkout", + attributes: attributes, + embeddedViews: embeddedViews, + config: config, + callbacks: callbacks) +``` + +**After (Swift):** + +```swift +MParticle.sharedInstance().rokt.selectPlacements("checkout", + attributes: attributes, + embeddedViews: embeddedViews, + config: config) { event in + switch event { + case is MPRoktEvent.MPRoktShowLoadingIndicator: + // Show loading indicator + case is MPRoktEvent.MPRoktHideLoadingIndicator: + // Hide loading indicator + case is MPRoktEvent.MPRoktPlacementReady: + // Handle load/ready + case is MPRoktEvent.MPRoktPlacementClosed: + // Handle unload/closed + case let sizeEvent as MPRoktEvent.MPRoktEmbeddedSizeChanged: + // Handle size change with sizeEvent.placementId and sizeEvent.updatedHeight + default: + break + } +} +``` + +##### purchaseFinalized Method + +**Before (Objective-C):** + +```objective-c +[[MParticle sharedInstance].rokt purchaseFinalized:@"checkout" + catalogItemId:@"item123" + success:YES]; +``` + +**After (Objective-C):** + +```objective-c +[[MParticle sharedInstance].rokt purchaseFinalized:@"checkout" + catalogItemId:@"item123" + success:YES]; +``` + +Note: The method signature remains the same, but the parameter name has changed from `placementId:` to `identifier:`. If you're using named parameters, update accordingly. + +##### New globalEvents Method + +The new `globalEvents:` method allows you to subscribe to global Rokt events from all sources, including events not associated with a specific view (such as `InitComplete`). + +**Objective-C:** + +```objective-c +[[MParticle sharedInstance].rokt globalEvents:^(MPRoktEvent * _Nonnull event) { + if ([event isKindOfClass:[MPRoktEvent.MPRoktInitComplete class]]) { + MPRoktEvent.MPRoktInitComplete *initEvent = (MPRoktEvent.MPRoktInitComplete *)event; + if (initEvent.success) { + // Rokt SDK initialized successfully + } + } +}]; +``` + +**Swift:** + +```swift +MParticle.sharedInstance().rokt.globalEvents { event in + if let initEvent = event as? MPRoktEvent.MPRoktInitComplete { + if initEvent.success { + // Rokt SDK initialized successfully + } + } +} +``` + +#### Event Mapping Reference + +| Old Callback | New Event Class | +| ------------------------------ | ---------------------------- | +| `onLoad` | `MPRoktPlacementReady` | +| `onUnLoad` | `MPRoktPlacementClosed` | +| `onShouldShowLoadingIndicator` | `MPRoktShowLoadingIndicator` | +| `onShouldHideLoadingIndicator` | `MPRoktHideLoadingIndicator` | +| `onEmbeddedSizeChange` | `MPRoktEmbeddedSizeChanged` | + +#### Notes + +- All `MPRoktEvent` subclasses are nested classes within `MPRoktEvent` +- The `onEvent` callback receives all event types, so use type checking to handle specific events +- The `MPRoktEmbeddedSizeChanged` event provides both `placementId` and `updatedHeight` properties +- Remove any references to `MPRoktEventCallback` from your code + +--- + ## Migrating from versions < 8.0.0 For migration guidance from SDK 7.x to SDK 8.x, please see [migration-guide-v8.md](migration-guide-v8.md). diff --git a/UnitTests/ObjCTests/MPRoktTests.m b/UnitTests/ObjCTests/MPRoktTests.m index 39021dac..4a201ba7 100644 --- a/UnitTests/ObjCTests/MPRoktTests.m +++ b/UnitTests/ObjCTests/MPRoktTests.m @@ -86,7 +86,7 @@ - (void)testSelectPlacementsSimpleWithValidParameters { // Set up expectations for kit container XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); OCMExpect([self.mockContainer forwardSDKCall:roktSelector event:nil parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { @@ -94,6 +94,7 @@ - (void)testSelectPlacementsSimpleWithValidParameters { XCTAssertEqualObjects(params[1], attributes); XCTAssertNil(params[2]); XCTAssertNil(params[3]); + XCTAssertNil(params[4]); return true; }] messageType:MPMessageTypeEvent @@ -126,12 +127,11 @@ - (void)testSelectPlacementsExpandedWithValidParameters { NSDictionary *finalAttributes = @{@"key": @"value", @"sandbox": @"true"}; MPRoktEmbeddedView *exampleView = [[MPRoktEmbeddedView alloc] initWithFrame:CGRectZero]; NSDictionary *embeddedViews = @{@"placement": exampleView}; - MPRoktEventCallback *exampleCallbacks = [[MPRoktEventCallback alloc] init]; - exampleCallbacks.onLoad = ^{}; - exampleCallbacks.onUnLoad = ^{}; - exampleCallbacks.onShouldShowLoadingIndicator = ^{}; - exampleCallbacks.onShouldHideLoadingIndicator = ^{}; - exampleCallbacks.onEmbeddedSizeChange = ^(NSString *p, CGFloat s){}; + + // Create onEvent callback block + void (^exampleOnEvent)(MPRoktEvent * _Nonnull) = ^(MPRoktEvent * _Nonnull event) { + // Handle event + }; MPRoktConfig *roktConfig = [[MPRoktConfig alloc] init]; roktConfig.colorMode = MPColorModeDark; @@ -140,7 +140,7 @@ - (void)testSelectPlacementsExpandedWithValidParameters { // Set up expectations for kit container XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); OCMExpect([self.mockContainer forwardSDKCall:roktSelector event:nil parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { @@ -148,12 +148,7 @@ - (void)testSelectPlacementsExpandedWithValidParameters { XCTAssertEqualObjects(params[1], finalAttributes); XCTAssertEqualObjects(params[2], embeddedViews); XCTAssertEqualObjects(params[3], roktConfig); - MPRoktEventCallback *resultCallbacks = params[4]; - XCTAssertEqualObjects(resultCallbacks.onLoad, exampleCallbacks.onLoad); - XCTAssertEqualObjects(resultCallbacks.onUnLoad, exampleCallbacks.onUnLoad); - XCTAssertEqualObjects(resultCallbacks.onShouldShowLoadingIndicator, exampleCallbacks.onShouldShowLoadingIndicator); - XCTAssertEqualObjects(resultCallbacks.onShouldHideLoadingIndicator, exampleCallbacks.onShouldHideLoadingIndicator); - XCTAssertEqualObjects(resultCallbacks.onEmbeddedSizeChange, exampleCallbacks.onEmbeddedSizeChange); + XCTAssertNotNil(params[4]); // onEvent callback should be set return true; }] messageType:MPMessageTypeEvent @@ -166,7 +161,7 @@ - (void)testSelectPlacementsExpandedWithValidParameters { attributes:attributes embeddedViews:embeddedViews config:roktConfig - callbacks:exampleCallbacks]; + onEvent:exampleOnEvent]; // Wait for async operation [self waitForExpectationsWithTimeout:0.2 handler:nil]; @@ -191,12 +186,12 @@ - (void)testSelectPlacementsExpandedWithNilParameters { attributes:nil embeddedViews:nil config:nil - callbacks:nil]; + onEvent:nil]; // Wait for async operation XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); NSDictionary *finalAttributes = @{@"sandbox": @"true"}; OCMExpect([self.mockContainer forwardSDKCall:roktSelector @@ -206,6 +201,7 @@ - (void)testSelectPlacementsExpandedWithNilParameters { XCTAssertEqualObjects(params[1], finalAttributes); XCTAssertNil(params[2]); XCTAssertNil(params[3]); + XCTAssertNil(params[4]); return true; }] messageType:MPMessageTypeEvent @@ -235,7 +231,7 @@ - (void)testSelectPlacementsSimpleWithMapping { // Set up expectations for kit container XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); OCMExpect([self.mockContainer forwardSDKCall:roktSelector event:nil parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { @@ -243,6 +239,7 @@ - (void)testSelectPlacementsSimpleWithMapping { XCTAssertEqualObjects(params[1], mappedAttributes); XCTAssertNil(params[2]); XCTAssertNil(params[3]); + XCTAssertNil(params[4]); return true; }] messageType:MPMessageTypeEvent @@ -269,7 +266,7 @@ - (void)testSelectPlacementsSimpleWithNilMapping { [[[self.mockInstance stub] andReturn:self.mockContainer] kitContainer_PRIVATE]; [[[self.mockInstance stub] andReturn:self.mockInstance] sharedInstance]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); OCMReject([self.mockContainer forwardSDKCall:roktSelector event:[OCMArg any] parameters:[OCMArg any] @@ -586,7 +583,7 @@ - (void)testPurchaseFinalized { [[[self.mockInstance stub] andReturn:self.mockInstance] sharedInstance]; // Set up test parameters - NSString *placementId = @"testonversion"; + NSString *identifier = @"testonversion"; NSString *catalogItemId = @"testcatalogItemId"; BOOL success = YES; @@ -596,7 +593,7 @@ - (void)testPurchaseFinalized { OCMExpect([self.mockContainer forwardSDKCall:roktSelector event:nil parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { - XCTAssertEqualObjects(params[0], placementId); + XCTAssertEqualObjects(params[0], identifier); XCTAssertEqualObjects(params[1], catalogItemId); XCTAssertEqualObjects(params[2], @(success)); return true; @@ -607,7 +604,7 @@ - (void)testPurchaseFinalized { }); // Execute method - [[MParticle sharedInstance].rokt purchaseFinalized:placementId catalogItemId:catalogItemId success:success]; + [[MParticle sharedInstance].rokt purchaseFinalized:identifier catalogItemId:catalogItemId success:success]; // Wait for async operation [self waitForExpectationsWithTimeout:0.2 handler:nil]; @@ -745,4 +742,91 @@ - (void)testEventsWithIdentifierCallbackInvocation { OCMVerifyAll(self.mockContainer); } +- (void)testGlobalEvents { + MParticle *instance = [MParticle sharedInstance]; + self.mockInstance = OCMPartialMock(instance); + self.mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]); + [[[self.mockInstance stub] andReturn:self.mockContainer] kitContainer_PRIVATE]; + [[[self.mockInstance stub] andReturn:self.mockInstance] sharedInstance]; + + // Set up test parameters + void (^onEventCallback)(MPRoktEvent *) = ^(MPRoktEvent *event) { + // Handle global event + }; + + // Set up expectations for kit container + XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; + SEL roktSelector = @selector(globalEvents:); + OCMExpect([self.mockContainer forwardSDKCall:roktSelector + event:nil + parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { + XCTAssertNotNil(params[0]); + return true; + }] + messageType:MPMessageTypeEvent + userInfo:nil]).andDo(^(NSInvocation *invocation) { + [expectation fulfill]; + }); + + // Execute method + [self.rokt globalEvents:onEventCallback]; + + // Wait for async operation + [self waitForExpectationsWithTimeout:0.2 handler:nil]; + + // Verify + OCMVerifyAll(self.mockContainer); +} + +- (void)testGlobalEventsCallbackInvocation { + MParticle *instance = [MParticle sharedInstance]; + self.mockInstance = OCMPartialMock(instance); + self.mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]); + [[[self.mockInstance stub] andReturn:self.mockContainer] kitContainer_PRIVATE]; + [[[self.mockInstance stub] andReturn:self.mockInstance] sharedInstance]; + + // Set up test parameters + __block BOOL callbackInvoked = NO; + __block MPRoktEvent *receivedEvent = nil; + + void (^onEventCallback)(MPRoktEvent *) = ^(MPRoktEvent *event) { + callbackInvoked = YES; + receivedEvent = event; + }; + + // Set up expectations for kit container to simulate callback invocation + XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"]; + SEL roktSelector = @selector(globalEvents:); + OCMExpect([self.mockContainer forwardSDKCall:roktSelector + event:nil + parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) { + // Simulate the kit calling the callback with InitComplete event + void (^capturedCallback)(MPRoktEvent *) = params[0]; + if (capturedCallback) { + MPRoktInitComplete *testEvent = [[MPRoktInitComplete alloc] initWithSuccess:YES]; + capturedCallback(testEvent); + } + return true; + }] + messageType:MPMessageTypeEvent + userInfo:nil]).andDo(^(NSInvocation *invocation) { + [expectation fulfill]; + }); + + // Execute method + [self.rokt globalEvents:onEventCallback]; + + // Wait for async operation + [self waitForExpectationsWithTimeout:0.2 handler:nil]; + + // Verify callback was invoked + XCTAssertTrue(callbackInvoked, @"Callback should have been invoked"); + XCTAssertNotNil(receivedEvent, @"Should have received an event"); + XCTAssertTrue([receivedEvent isKindOfClass:[MPRoktInitComplete class]], @"Should receive the correct event type"); + XCTAssertTrue(((MPRoktInitComplete *)receivedEvent).success, @"InitComplete event should indicate success"); + + // Verify + OCMVerifyAll(self.mockContainer); +} + @end diff --git a/mParticle-Apple-SDK/Include/MPKitProtocol.h b/mParticle-Apple-SDK/Include/MPKitProtocol.h index 02e5d420..07adac98 100644 --- a/mParticle-Apple-SDK/Include/MPKitProtocol.h +++ b/mParticle-Apple-SDK/Include/MPKitProtocol.h @@ -17,7 +17,6 @@ @class FilteredMPIdentityApiRequest; @class MPRoktEmbeddedView; @class MPRoktConfig; -@class MPRoktEventCallback; @class MPRoktEvent; #if TARGET_OS_IOS == 1 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @@ -132,17 +131,18 @@ #pragma mark First Party Kits - (nonnull MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier - attributes:(NSDictionary * _Nonnull)attributes - embeddedViews:(NSDictionary * _Nullable)embeddedViews - config:(MPRoktConfig * _Nullable)config - callbacks:(MPRoktEventCallback * _Nullable)callbacks - filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser; + attributes:(NSDictionary * _Nonnull)attributes + embeddedViews:(NSDictionary * _Nullable)embeddedViews + config:(MPRoktConfig * _Nullable)config + onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent + filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser; - (nonnull MPKitExecStatus *)setWrapperSdk:(MPWrapperSdk)wrapperSdk version:(nonnull NSString *)wrapperSdkVersion; -- (nonnull MPKitExecStatus *)purchaseFinalized:(nonnull NSString *)placementId +- (nonnull MPKitExecStatus *)purchaseFinalized:(nonnull NSString *)identifier catalogItemId:(nonnull NSString *)catalogItemId success:(nonnull NSNumber *)success; - (nonnull MPKitExecStatus *)events:(NSString * _Nonnull)identifier onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent; +- (nonnull MPKitExecStatus *)globalEvents:(void (^ _Nonnull)(MPRoktEvent * _Nonnull))onEvent; @end diff --git a/mParticle-Apple-SDK/Include/MPRokt.h b/mParticle-Apple-SDK/Include/MPRokt.h index 78f6ff08..b70c1d35 100644 --- a/mParticle-Apple-SDK/Include/MPRokt.h +++ b/mParticle-Apple-SDK/Include/MPRokt.h @@ -8,25 +8,6 @@ #import #import -/** - * Callback container for Rokt callbacks. - * Used to handle various lifecycle and UI events from Rokt. - */ -@interface MPRoktEventCallback : NSObject - -/** Called when the Rokt placement has finished loading */ -@property (nonatomic, copy, nullable) void (^onLoad)(void); -/** Called when the Rokt placement is being unloaded/removed */ -@property (nonatomic, copy, nullable) void (^onUnLoad)(void); -/** Called when Rokt reccomends the UI shows a loading indicator */ -@property (nonatomic, copy, nullable) void (^onShouldShowLoadingIndicator)(void); -/** Called when Rokt reccomends the UI hides its loading indicator */ -@property (nonatomic, copy, nullable) void (^onShouldHideLoadingIndicator)(void); -/** Called when the embedded view's size changes */ -@property (nonatomic, copy, nullable) void (^onEmbeddedSizeChange)(NSString * _Nonnull, CGFloat); - -@end - /** * Custom view class for embedding Rokt widgets in the UI. * Inherits from UIView and provides container functionality for Rokt placements. @@ -77,38 +58,47 @@ typedef NS_ENUM(NSInteger, MPColorMode) { attributes:(NSDictionary * _Nullable)attributes; /** - * Selects a Rokt placement with full configuration options including embedded views and callbacks. + * Selects a Rokt placement with full configuration options including embedded views and event callback. * * @param identifier Unique identifier for the placement * @param attributes Optional dictionary of attributes to customize the placement * @param embeddedViews Optional dictionary mapping placement names to their embedded views - * @param roktEventCallback Optional callback object to handle widget events + * @param config Optional configuration object for customizing the placement display + * @param onEvent Optional callback block to handle Rokt events */ - (void)selectPlacements:(NSString *_Nonnull)identifier attributes:(NSDictionary * _Nullable)attributes embeddedViews:(NSDictionary * _Nullable)embeddedViews config:(MPRoktConfig * _Nullable)config - callbacks:(MPRoktEventCallback * _Nullable)roktEventCallback; + onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent; /** * Used to report a successful conversion without displaying a placement * - * @param placementId Unique identifier for the placement + * @param identifier Unique identifier for the placement * @param catalogItemId Unique identifier for the catalog item ID * @param success Indicates whether or not the purchase was successful */ -- (void)purchaseFinalized:(NSString *_Nonnull)placementId +- (void)purchaseFinalized:(NSString *_Nonnull)identifier catalogItemId:(NSString *_Nonnull)catalogItemId success:(BOOL)success; /** - * Used to subscribe to Rokt events + * Used to subscribe to Rokt events for a specific placement * * @param identifier The identifier of the placement to subscribe to * @param onEvent The block to execute when the event is triggered */ - (void)events:(NSString *_Nonnull)identifier onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent; +/** + * Used to subscribe to global Rokt events from all sources. + * Additional events that are not associated with a view (such as InitComplete) will also be delivered. + * + * @param onEvent The block to execute when the event is triggered + */ +- (void)globalEvents:(void (^ _Nonnull)(MPRoktEvent * _Nonnull))onEvent; + /** * Used to close Rokt overlay placements */ diff --git a/mParticle-Apple-SDK/Kits/MPKitContainer.m b/mParticle-Apple-SDK/Kits/MPKitContainer.m index a7754e72..854a9a72 100644 --- a/mParticle-Apple-SDK/Kits/MPKitContainer.m +++ b/mParticle-Apple-SDK/Kits/MPKitContainer.m @@ -2303,7 +2303,7 @@ - (void)attemptToLogEventToKit:(id)kitRegister kitFilter return; } execStatus = [kitRegister.wrapperInstance logEvent:((MPEvent *)kitFilter.forwardEvent)]; - } else if (selector == @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:)) { + } else if (selector == @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:)) { if (kitFilter.shouldFilter) { return; } @@ -2313,7 +2313,7 @@ - (void)attemptToLogEventToKit:(id)kitRegister kitFilter attributes:parameters[1] embeddedViews:parameters[2] config:parameters[3] - callbacks:parameters[4] + onEvent:parameters[4] filteredUser:filteredUser]; } else if (selector == @selector(logScreen:)) { if (!kitFilter.forwardEvent || ![kitFilter.forwardEvent isKindOfClass:[MPEvent class]]) { diff --git a/mParticle-Apple-SDK/MPRokt.m b/mParticle-Apple-SDK/MPRokt.m index 6b5c1270..16d00d99 100644 --- a/mParticle-Apple-SDK/MPRokt.m +++ b/mParticle-Apple-SDK/MPRokt.m @@ -29,9 +29,6 @@ + (dispatch_queue_t)messageQueue; @end -@implementation MPRoktEventCallback -@end - @implementation MPRoktEmbeddedView @end @@ -47,7 +44,7 @@ @implementation MPRokt /// - attributes: Optional dictionary of user attributes to pass to Rokt (e.g., email, firstName, etc.) - (void)selectPlacements:(NSString *)identifier attributes:(NSDictionary * _Nullable)attributes { - [self selectPlacements:identifier attributes:attributes embeddedViews:nil config:nil callbacks:nil]; + [self selectPlacements:identifier attributes:attributes embeddedViews:nil config:nil onEvent:nil]; } /// Displays a Rokt ad placement with full configuration options. @@ -58,12 +55,12 @@ - (void)selectPlacements:(NSString *)identifier /// - attributes: Optional dictionary of user attributes (email, firstName, etc.). Attributes will be mapped according to dashboard configuration. /// - embeddedViews: Optional dictionary mapping placement identifiers to embedded view containers for inline placements /// - config: Optional Rokt configuration object (e.g., for dark mode or custom styling) -/// - callbacks: Optional callback handlers for Rokt events (selection, display, completion, etc.) +/// - onEvent: Optional callback block to handle Rokt events - (void)selectPlacements:(NSString *)identifier attributes:(NSDictionary * _Nullable)attributes embeddedViews:(NSDictionary * _Nullable)embeddedViews config:(MPRoktConfig * _Nullable)config - callbacks:(MPRoktEventCallback * _Nullable)callbacks { + onEvent:(void (^ _Nullable)(MPRoktEvent * _Nonnull))onEvent { MParticleUser *currentUser = [MParticle sharedInstance].identity.currentUser; // If email is passed in as an attribute and it's different than the existing identity, identify with it @@ -95,9 +92,9 @@ - (void)selectPlacements:(NSString *)identifier [queueParameters addParameter:[self confirmSandboxAttribute:mappedAttributes]]; [queueParameters addParameter:embeddedViews]; [queueParameters addParameter:config]; - [queueParameters addParameter:callbacks]; + [queueParameters addParameter:onEvent]; - SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:callbacks:filteredUser:); + SEL roktSelector = @selector(executeWithIdentifier:attributes:embeddedViews:config:onEvent:filteredUser:); [[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector event:nil parameters:queueParameters @@ -114,14 +111,14 @@ - (void)selectPlacements:(NSString *)identifier /// Notifies Rokt that a purchase from a placement offer has been finalized. /// Call this method to inform Rokt about the completion status of an offer purchase initiated from a placement. /// - Parameters: -/// - placementId: The identifier of the placement where the offer was displayed +/// - identifier: The identifier of the placement where the offer was displayed /// - catalogItemId: The identifier of the catalog item that was purchased /// - success: Whether the purchase was successful (YES) or failed (NO) -- (void)purchaseFinalized:(NSString * _Nonnull)placementId catalogItemId:(NSString * _Nonnull)catalogItemId success:(BOOL)success { +- (void)purchaseFinalized:(NSString * _Nonnull)identifier catalogItemId:(NSString * _Nonnull)catalogItemId success:(BOOL)success { dispatch_async(dispatch_get_main_queue(), ^{ // Forwarding call to kits MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init]; - [queueParameters addParameter:placementId]; + [queueParameters addParameter:identifier]; [queueParameters addParameter:catalogItemId]; [queueParameters addParameter:@(success)]; @@ -156,6 +153,26 @@ - (void)events:(NSString * _Nonnull)identifier onEvent:(void (^ _Nullable)(MPRok }); } +/// Registers a callback to receive global events from all Rokt sources. +/// Additional events that are not associated with a view (such as InitComplete) will also be delivered. +/// - Parameters: +/// - onEvent: Callback block that receives MPRoktEvent objects when events occur +- (void)globalEvents:(void (^ _Nonnull)(MPRoktEvent * _Nonnull))onEvent { + dispatch_async(dispatch_get_main_queue(), ^{ + // Forwarding call to kits + MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init]; + [queueParameters addParameter:onEvent]; + + SEL roktSelector = @selector(globalEvents:); + [[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector + event:nil + parameters:queueParameters + messageType:MPMessageTypeEvent + userInfo:nil + ]; + }); +} + /// Closes any currently displayed Rokt placement. /// Call this method to programmatically dismiss an active Rokt overlay or embedded placement. - (void)close { diff --git a/mParticle-Apple-SDK/MPRoktEvent.swift b/mParticle-Apple-SDK/MPRoktEvent.swift index f33d99d1..eb2993aa 100644 --- a/mParticle-Apple-SDK/MPRoktEvent.swift +++ b/mParticle-Apple-SDK/MPRoktEvent.swift @@ -8,6 +8,7 @@ // import Foundation +import UIKit @objc public class MPRoktEvent: NSObject { @objc public class MPRoktInitComplete: MPRoktEvent { @@ -156,4 +157,22 @@ import Foundation super.init() } } + + /// An event that is emitted when the height of an embedded placement changes. + /// This event is useful for dynamically adjusting the container view's height + /// to accommodate the Rokt placement content. + /// + /// - Note: This event is only emitted for embedded placements + @objc public class MPRoktEmbeddedSizeChanged: MPRoktEvent { + /// The identifier of the placement whose height has changed + @objc public let placementId: String + /// The new height of the placement + @objc public let updatedHeight: CGFloat + + @objc public init(placementId: String, updatedHeight: CGFloat) { + self.placementId = placementId + self.updatedHeight = updatedHeight + super.init() + } + } }