Skip to content

Commit c4737f7

Browse files
committed
feat: Update Rokt-SDK dependency and add PlacementOptions
- Updated Rokt SDK to 4.16.1 with PlacementOptions parameter support - Introduced MPRoktPlacementOptions to capture placement options during execution. - Changed MPKitRokt methods to accept placement options. - Added tests to verify timestamp capture and default options behavior in MPRoktLayout.
1 parent 392c33c commit c4737f7

7 files changed

Lines changed: 134 additions & 10 deletions

File tree

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let package = Package(
1717
.upToNextMajor(from: "8.0.0")),
1818
.package(name: "Rokt-Widget",
1919
url: "https://github.com/ROKT/rokt-sdk-ios",
20-
.upToNextMajor(from: "4.16.0")),
20+
.upToNextMajor(from: "4.16.1")),
2121
],
2222
targets: [
2323
.target(

mParticle-Rokt-Swift/MPRoktLayout.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import mParticle_Rokt
1919
@available(iOS 15, *)
2020
public class MPRoktLayout {
2121
public var roktLayout: RoktLayout? = nil
22+
public private(set) var placementOptions: PlacementOptions? = nil
2223
let mparticle = MParticle.sharedInstance()
2324

2425
public init(
@@ -29,6 +30,13 @@ public class MPRoktLayout {
2930
config: RoktConfig? = nil,
3031
onEvent: ((RoktEvent) -> Void)? = nil
3132
) {
33+
// Capture the timestamp when the SwiftUI component is rendered
34+
let options = PlacementOptions(
35+
jointSdkSelectPlacements: Int64(Date().timeIntervalSince1970 * 1000),
36+
dynamicPerformanceMarkers: [:]
37+
)
38+
self.placementOptions = options
39+
3240
MPRoktLayout.mpLog("Initializing MPRoktLayout with arguments sdkTriggered:\(sdkTriggered.wrappedValue), viewName:\(viewName ?? "nil"), locationName:\(locationName), attributes:\(attributes)")
3341
confirmUser(attributes: attributes) { identifyCalled in
3442
let preparedAttributes = MPKitRokt.prepareAttributes(attributes, filteredUser: Optional<FilteredMParticleUser>.none, performMapping: true)
@@ -43,6 +51,7 @@ public class MPRoktLayout {
4351
locationName: locationName,
4452
attributes: preparedAttributes,
4553
config: config,
54+
placementOptions: options,
4655
onEvent: onEvent
4756
)
4857
// The Binding variable provided by the client allows us to trigger a re-render of the UI but we only want to do this if the value was true to start

mParticle-Rokt.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ Pod::Spec.new do |s|
2121
objc.source_files = 'mParticle-Rokt/*.{h,m}'
2222
objc.public_header_files = 'mParticle-Rokt/*.h'
2323
objc.dependency 'mParticle-Apple-SDK', '~> 8.0'
24-
objc.dependency 'Rokt-Widget', '~> 4.15'
24+
objc.dependency 'Rokt-Widget', '~> 4.16'
2525
end
2626

2727
# Swift subspec
2828
s.subspec 'Swift' do |swift|
2929
swift.source_files = 'mParticle-Rokt-Swift/*.swift'
3030
swift.dependency 'mParticle-Rokt/ObjC'
3131
swift.dependency 'mParticle-Apple-SDK', '~> 8.0'
32-
swift.dependency 'Rokt-Widget', '~> 4.15'
32+
swift.dependency 'Rokt-Widget', '~> 4.16'
3333
end
3434

3535
# Default includes both

mParticle-Rokt/MPKitRokt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#import "mParticle_Apple_SDK-Swift.h"
1111
#endif
1212

13+
@class MPRoktPlacementOptions;
14+
1315
@interface MPKitRokt : NSObject <MPKitProtocol>
1416

1517
@property (nonatomic, strong, nonnull) NSDictionary *configuration;

mParticle-Rokt/MPKitRokt.m

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier
120120
embeddedViews:(NSDictionary<NSString *, MPRoktEmbeddedView *> * _Nullable)embeddedViews
121121
config:(MPRoktConfig * _Nullable)mpRoktConfig
122122
callbacks:(MPRoktEventCallback * _Nullable)callbacks
123-
filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser {
124-
[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]];
123+
filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser
124+
options:(MPRoktPlacementOptions * _Nullable)options {
125+
[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: %@ \n options: %@", identifier, attributes, embeddedViews, mpRoktConfig, callbacks, filteredUser.userIdentities, options]];
125126
NSDictionary<NSString *, NSString *> *finalAtt = [MPKitRokt prepareAttributes:attributes filteredUser:filteredUser performMapping:NO];
126127

127128
// Log custom event for selectPlacements call
@@ -130,11 +131,17 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier
130131
//Convert MPRoktConfig to RoktConfig
131132
RoktConfig *roktConfig = [MPKitRokt convertMPRoktConfig:mpRoktConfig];
132133
NSDictionary<NSString *, RoktEmbeddedView *> *confirmedViews = [self confirmEmbeddedViews:embeddedViews];
134+
135+
PlacementOptions *placementOptions = [[PlacementOptions alloc] initWithJointSdkSelectPlacements:0 dynamicPerformanceMarkers:@{}];
136+
if (options) {
137+
placementOptions = [[PlacementOptions alloc] initWithJointSdkSelectPlacements:options.jointSdkSelectPlacements dynamicPerformanceMarkers:@{}];
138+
}
133139

134140
[Rokt executeWithViewName:identifier
135141
attributes:finalAtt
136142
placements:confirmedViews
137143
config:roktConfig
144+
placementOptions:placementOptions
138145
onLoad:callbacks.onLoad
139146
onUnLoad:callbacks.onUnLoad
140147
onShouldShowLoadingIndicator:callbacks.onShouldShowLoadingIndicator

mParticle_RoktTests/mParticle_RoktTests.m

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ - (MPKitExecStatus *)executeWithIdentifier:(NSString * _Nullable)identifier
1414
embeddedViews:(NSDictionary<NSString *, MPRoktEmbeddedView *> * _Nullable)embeddedViews
1515
config:(MPRoktConfig * _Nullable)mpRoktConfig
1616
callbacks:(MPRoktEventCallback * _Nullable)callbacks
17-
filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser;
17+
filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser
18+
options:(MPRoktPlacementOptions * _Nullable)options;
1819
- (MPKitExecStatus *)setWrapperSdk:(MPWrapperSdk)wrapperSdk version:(nonnull NSString *)wrapperSdkVersion;
1920

2021
- (MPKitExecStatus *)purchaseFinalized:(NSString *)placementId
@@ -164,6 +165,7 @@ - (void)testExecuteWithIdentifier {
164165
}]
165166
placements:OCMOCK_ANY
166167
config:nil
168+
placementOptions:OCMOCK_ANY
167169
onLoad:nil
168170
onUnLoad:nil
169171
onShouldShowLoadingIndicator:nil
@@ -175,7 +177,8 @@ - (void)testExecuteWithIdentifier {
175177
embeddedViews:embeddedViews
176178
config:nil
177179
callbacks:nil
178-
filteredUser:user];
180+
filteredUser:user
181+
options:nil];
179182

180183
// Verify
181184
XCTAssertNotNil(status);
@@ -201,6 +204,7 @@ - (void)testExecuteSandboxDetection {
201204
}]
202205
placements:OCMOCK_ANY
203206
config:nil
207+
placementOptions:OCMOCK_ANY
204208
onLoad:nil
205209
onUnLoad:nil
206210
onShouldShowLoadingIndicator:nil
@@ -212,7 +216,83 @@ - (void)testExecuteSandboxDetection {
212216
embeddedViews:embeddedViews
213217
config:nil
214218
callbacks:nil
215-
filteredUser:user];
219+
filteredUser:user
220+
options:nil];
221+
222+
// Verify
223+
XCTAssertNotNil(status);
224+
XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess);
225+
OCMVerifyAll(mockRoktSDK);
226+
}
227+
228+
- (void)testExecuteWithIdentifierWithOptions {
229+
id mockRoktSDK = OCMClassMock([Rokt class]);
230+
231+
MPRoktEmbeddedView *view = [[MPRoktEmbeddedView alloc] init];
232+
NSString *identifier = @"TestView";
233+
NSDictionary *embeddedViews = @{@"placement1": view};
234+
NSDictionary *attributes = @{@"attr1": @"value1", @"sandbox": @"false"};
235+
FilteredMParticleUser *user = [[FilteredMParticleUser alloc] init];
236+
237+
// Create placement options with a custom timestamp value
238+
MPRoktPlacementOptions *options = [[MPRoktPlacementOptions alloc] initWithTimestamp:42];
239+
240+
// Expect Rokt execute call and verify placementOptions carries the jointSdkSelectPlacements value
241+
OCMExpect([mockRoktSDK executeWithViewName:identifier
242+
attributes:OCMOCK_ANY
243+
placements:OCMOCK_ANY
244+
config:nil
245+
placementOptions:[OCMArg checkWithBlock:^BOOL(PlacementOptions *opts) {
246+
return opts != nil;
247+
}]
248+
onLoad:nil
249+
onUnLoad:nil
250+
onShouldShowLoadingIndicator:nil
251+
onShouldHideLoadingIndicator:nil
252+
onEmbeddedSizeChange:nil]);
253+
254+
MPKitExecStatus *status = [self.kitInstance executeWithIdentifier:identifier
255+
attributes:attributes
256+
embeddedViews:embeddedViews
257+
config:nil
258+
callbacks:nil
259+
filteredUser:user
260+
options:options];
261+
262+
// Verify
263+
XCTAssertNotNil(status);
264+
XCTAssertEqual(status.returnCode, MPKitReturnCodeSuccess);
265+
OCMVerifyAll(mockRoktSDK);
266+
}
267+
268+
- (void)testExecuteWithIdentifierNilOptionsCreatesDefaultPlacementOptions {
269+
id mockRoktSDK = OCMClassMock([Rokt class]);
270+
271+
NSString *identifier = @"TestView";
272+
NSDictionary *attributes = @{@"attr1": @"value1", @"sandbox": @"false"};
273+
FilteredMParticleUser *user = [[FilteredMParticleUser alloc] init];
274+
275+
// When options is nil, a default PlacementOptions with jointSdkSelectPlacements=0 should be created
276+
OCMExpect([mockRoktSDK executeWithViewName:identifier
277+
attributes:OCMOCK_ANY
278+
placements:OCMOCK_ANY
279+
config:nil
280+
placementOptions:[OCMArg checkWithBlock:^BOOL(PlacementOptions *opts) {
281+
return opts != nil;
282+
}]
283+
onLoad:nil
284+
onUnLoad:nil
285+
onShouldShowLoadingIndicator:nil
286+
onShouldHideLoadingIndicator:nil
287+
onEmbeddedSizeChange:nil]);
288+
289+
MPKitExecStatus *status = [self.kitInstance executeWithIdentifier:identifier
290+
attributes:attributes
291+
embeddedViews:nil
292+
config:nil
293+
callbacks:nil
294+
filteredUser:user
295+
options:nil];
216296

217297
// Verify
218298
XCTAssertNotNil(status);
@@ -783,6 +863,7 @@ - (void)testExecuteWithIdentifierLogsSelectPlacementEventWithPreparedAttributes
783863
attributes:OCMOCK_ANY
784864
placements:OCMOCK_ANY
785865
config:OCMOCK_ANY
866+
placementOptions:OCMOCK_ANY
786867
onLoad:OCMOCK_ANY
787868
onUnLoad:OCMOCK_ANY
788869
onShouldShowLoadingIndicator:OCMOCK_ANY
@@ -795,7 +876,8 @@ - (void)testExecuteWithIdentifierLogsSelectPlacementEventWithPreparedAttributes
795876
embeddedViews:nil
796877
config:nil
797878
callbacks:nil
798-
filteredUser:user];
879+
filteredUser:user
880+
options:nil];
799881

800882
// Verify that logEvent was called with the correct MPEvent object
801883
OCMVerifyAll(mockMParticleInstance);

mParticle_RoktTests/mParticle_Rokt_SwiftTests.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,31 @@ struct mParticle_Rokt_SwiftTests {
284284
// Then
285285
#expect(true, "logSelectPlacementEvent should handle MParticle instance state gracefully")
286286
}
287-
287+
288+
@MainActor @available(iOS 15, *)
289+
@Test func testMPRoktLayoutCapturesTimestampOnInit() {
290+
// Given
291+
let sdkTriggered = Binding.constant(false)
292+
let attributes: [String: String] = ["test": "value"]
293+
let beforeTimestamp = Int64(Date().timeIntervalSince1970 * 1000)
294+
295+
// When
296+
let layout = MPRoktLayout(
297+
sdkTriggered: sdkTriggered,
298+
locationName: "timestamp_test",
299+
attributes: attributes
300+
)
301+
302+
let afterTimestamp = Int64(Date().timeIntervalSince1970 * 1000)
303+
304+
// Then - Verify placementOptions was created with a valid timestamp
305+
#expect(layout.placementOptions != nil, "PlacementOptions should be set on init")
306+
let capturedTimestamp = layout.placementOptions!.jointSdkSelectPlacements
307+
#expect(capturedTimestamp >= beforeTimestamp, "Captured timestamp should be at or after the before-init time")
308+
#expect(capturedTimestamp <= afterTimestamp, "Captured timestamp should be at or before the after-init time")
309+
#expect(capturedTimestamp > 0, "Captured timestamp should be a positive millisecond epoch value")
310+
}
311+
288312
// MARK: - Integration Tests
289313

290314
@MainActor @available(iOS 15, *)

0 commit comments

Comments
 (0)