Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ For each release, **Core** (main SDK) changes are listed first, followed by **Ki

## [Unreleased]

### Kits

#### Rokt

##### Added

- Report non-sensitive mParticle public API usage diagnostics when the Rokt kit is active.

## [9.3.4] - 2026-08-07

### Kits
Expand Down
4 changes: 2 additions & 2 deletions Kits/rokt/rokt/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let useLocalVersion = ProcessInfo.processInfo.environment["USE_LOCAL_VERSION"] !

let mParticleAppleSDK: Package.Dependency = {
if useLocalVersion {
return .package(path: "../../../")
return .package(name: "mparticle-apple-sdk", path: "../../../")
}

let url = "https://github.com/mParticle/mparticle-apple-sdk"
Expand All @@ -32,7 +32,7 @@ let package = Package(
mParticleAppleSDK,
.package(
url: "https://github.com/ROKT/rokt-sdk-ios",
.upToNextMajor(from: "5.3.0")
.upToNextMajor(from: "5.3.2")
),
.package(
url: "https://github.com/ROKT/rokt-contracts-apple.git",
Expand Down
5 changes: 5 additions & 0 deletions Kits/rokt/rokt/Sources/mParticle-Rokt/MPKitRokt.m
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,11 @@ - (NSString *)getSessionId {
return [Rokt getSessionId];
}

/// Forwards a bounded public-API-usage diagnostic code from mParticle core into the Rokt SDK.
- (void)logMParticleApiDiagnostic:(NSString *)code {
[Rokt logMParticleApiCall:code additionalInfo:@{}];
}

+ (void)MPLog:(NSString *)string {
NSString *msg = [NSString stringWithFormat:@"%@%@", @"MPRokt -> ", string];
if ([[MParticle sharedInstance] environment] == MPEnvironmentDevelopment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ - (void)testStopResetsKitState {
[mockRoktSDK stopMocking];
}

- (void)testLogMParticleApiDiagnostic_forwardsCode {
id mockRoktSDK = OCMClassMock([Rokt class]);
OCMExpect([mockRoktSDK logMParticleApiCall:@"LOG_EVENT" additionalInfo:@{}]);

[self.kitInstance logMParticleApiDiagnostic:@"LOG_EVENT"];

OCMVerifyAll(mockRoktSDK);
[mockRoktSDK stopMocking];
}

- (void)testStartCanBeCalledAgainAfterStop {
id mockRoktSDK = OCMClassMock([Rokt class]);
OCMStub([mockRoktSDK close]);
Expand Down
2 changes: 1 addition & 1 deletion Kits/rokt/rokt/mParticle-Rokt.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ Pod::Spec.new do |s|
s.ios.resource_bundles = { 'mParticle-Rokt-Privacy' => ['Sources/mParticle-Rokt/PrivacyInfo.xcprivacy'] }
s.ios.dependency 'mParticle-Apple-SDK', '~> 9.1'
s.ios.dependency 'RoktContracts', '~> 2.0'
s.ios.dependency 'Rokt-Widget', '~> 5.3'
s.ios.dependency 'Rokt-Widget', '~> 5.3.2'
end
2 changes: 1 addition & 1 deletion Kits/rokt/rokt/mParticle-Rokt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@
repositoryURL = "https://github.com/ROKT/rokt-sdk-ios";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 5.0.0;
minimumVersion = 5.3.2;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
39 changes: 39 additions & 0 deletions UnitTests/ObjCTests/MPRoktTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
// Test helper class that simulates a kit with getSessionId and handleURLCallback methods
@interface MPRoktTestKitInstance : NSObject
@property (nonatomic, copy) NSString *sessionIdToReturn;
@property (nonatomic, copy) NSString *lastDiagnosticCode;
@property (nonatomic, assign) BOOL handleURLCallbackReturn;
@property (nonatomic, strong) NSURL *lastHandleURLCallbackURL;
- (NSString *)getSessionId;
- (void)logMParticleApiDiagnostic:(NSString *)code;
- (BOOL)handleURLCallback:(NSURL *)url;
@end

@implementation MPRoktTestKitInstance
- (NSString *)getSessionId {
return self.sessionIdToReturn;
}
- (void)logMParticleApiDiagnostic:(NSString *)code {
self.lastDiagnosticCode = code;
}
- (BOOL)handleURLCallback:(NSURL *)url {
self.lastHandleURLCallbackURL = url;
return self.handleURLCallbackReturn;
Expand All @@ -43,6 +48,7 @@ - (void)confirmUser:(NSDictionary<NSString *, NSString *> *)attributes user:(MPa
@end

@interface MPRokt (Testing)
- (void)logRoktApiDiagnostic:(NSString *)code;
@end

@interface MParticle ()
Expand Down Expand Up @@ -1122,6 +1128,39 @@ - (void)testSetSessionIdForwardsToKitContainer {
OCMVerifyAll(self.mockContainer);
}

#pragma mark - Public API Diagnostics Tests

- (void)testLogRoktApiDiagnosticForwardsToActiveRoktKit {
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];

id mockKitRegister = OCMProtocolMock(@protocol(MPExtensionKitProtocol));
OCMStub([(id<MPExtensionKitProtocol>)mockKitRegister code]).andReturn(kTestRoktKitId);
MPRoktTestKitInstance *kitInstance = [[MPRoktTestKitInstance alloc] init];
OCMStub([mockKitRegister wrapperInstance]).andReturn(kitInstance);
OCMStub([self.mockContainer activeKitsRegistry]).andReturn(@[mockKitRegister]);

[self.rokt logRoktApiDiagnostic:@"LOG_EVENT"];

XCTAssertEqualObjects(kitInstance.lastDiagnosticCode, @"LOG_EVENT");
}

- (void)testLogRoktApiDiagnosticDoesNothingWithoutActiveRoktKit {
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];
OCMStub([self.mockContainer activeKitsRegistry]).andReturn(@[]);

[self.rokt logRoktApiDiagnostic:@"LOG_EVENT"];

OCMVerify([self.mockContainer activeKitsRegistry]);
}

#pragma mark - getSessionId Tests

- (void)testGetSessionIdReturnsSessionIdFromKit {
Expand Down
7 changes: 7 additions & 0 deletions mParticle-Apple-SDK/Identity/MPIdentityApi.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "MPKitContainer.h"
#import "MPUpload.h"
#import "MPUserDefaultsConnector.h"
#import "../MPRokt+MParticlePrivate.h"
@import mParticle_Apple_SDK_Swift;

typedef NS_ENUM(NSUInteger, MPIdentityRequestType) {
Expand Down Expand Up @@ -351,6 +352,7 @@ - (MParticleUser *)getUser:(NSNumber *)mpId {
}

- (NSArray<MParticleUser *> *)getAllUsers {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"GET_USERS"];
MPUserDefaults *userDefaults = MPUserDefaultsConnector.userDefaults;
NSMutableArray<MParticleUser *> *userArray = [[NSMutableArray alloc] init];

Expand All @@ -375,6 +377,7 @@ - (void)identifyNoDispatch:(MPIdentityApiRequest *)identifyRequest completion:(n
}

- (void)identify:(MPIdentityApiRequest *)identifyRequest completion:(nullable MPIdentityApiResultCallback)completion {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"IDENTIFY"];
MPIdentityApiResultCallback wrappedCompletion = ^(MPIdentityApiResult * _Nullable apiResult, NSError * _Nullable error) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand All @@ -392,6 +395,7 @@ - (void)identifyWithCompletion:(nullable MPIdentityApiResultCallback)completion
}

- (void)login:(MPIdentityApiRequest *)loginRequest completion:(nullable MPIdentityApiResultCallback)completion {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"LOGIN"];
MPIdentityApiResultCallback wrappedCompletion = ^(MPIdentityApiResult *_Nullable apiResult, NSError *_Nullable error) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand All @@ -414,6 +418,7 @@ - (void)loginWithCompletion:(nullable MPIdentityApiResultCallback)completion {
}

- (void)logout:(MPIdentityApiRequest *)logoutRequest completion:(nullable MPIdentityApiResultCallback)completion {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"LOGOUT"];
MPIdentityApiResultCallback wrappedCompletion = ^(MPIdentityApiResult *_Nullable apiResult, NSError *_Nullable error) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand All @@ -434,6 +439,7 @@ - (void)logoutWithCompletion:(nullable MPIdentityApiResultCallback)completion {
}

- (void)modify:(MPIdentityApiRequest *)modifyRequest completion:(nullable MPModifyApiResultCallback)completion {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"MODIFY"];
MPModifyApiResultCallback wrappedCompletion = ^(MPModifyApiResult *_Nullable apiResult, NSError *_Nullable error) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand All @@ -449,6 +455,7 @@ - (void)modify:(MPIdentityApiRequest *)modifyRequest completion:(nullable MPModi
}

- (BOOL)aliasUsers:(MPAliasRequest *)aliasRequest {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"ALIAS_USERS"];
if (aliasRequest.sourceMPID == nil || aliasRequest.destinationMPID == nil || aliasRequest.sourceMPID.longLongValue == 0 || aliasRequest.destinationMPID.longLongValue == 0 || [aliasRequest.sourceMPID isEqual:aliasRequest.destinationMPID]) {
MPILogError(@"Invalid alias request - both users must exist and not be equal.");
return NO;
Expand Down
10 changes: 9 additions & 1 deletion mParticle-Apple-SDK/Identity/MParticleUser.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "MPIConstants.h"
#import "MPKitContainer.h"
#import "MPUserDefaultsConnector.h"
#import "../MPRokt+MParticlePrivate.h"
@import mParticle_Apple_SDK_Swift;

@interface MParticleUser ()
Expand Down Expand Up @@ -216,6 +217,7 @@ - (BOOL)forwardLegacyUserIdentityToKitContainer:(NSString *)identityString ident
}

- (nullable NSNumber *)incrementUserAttribute:(NSString *)key byValue:(NSNumber *)value {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"INCREMENT_USER_ATTRIBUTE"];
dispatch_async([MParticle messageQueue], ^{
MPStateMachine_PRIVATE *stateMachine = [MParticle sharedInstance].stateMachine;
if (stateMachine.optOut) {
Expand Down Expand Up @@ -312,6 +314,7 @@ - (void)setUserAttribute:(nonnull NSString *)key value:(nonnull id)value {
}

- (void)setUserAttributeList:(nonnull NSString *)key values:(nonnull NSArray<NSString *> *)values {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"SET_USER_ATTRIBUTE_LIST"];
if (values.count == 0) {
MPILogDebug(@"User attribute not updated. Please use removeUserAttribute.");
return;
Expand Down Expand Up @@ -364,6 +367,7 @@ - (void)setUserAttributeList:(nonnull NSString *)key values:(nonnull NSArray<NSS
}

- (void)setUserTag:(nonnull NSString *)tag {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"SET_USER_TAG"];
__weak MParticleUser *weakSelf = self;
NSDate *timestamp = [NSDate date];
dispatch_async([MParticle messageQueue], ^{
Expand Down Expand Up @@ -399,6 +403,7 @@ - (void)setUserTag:(nonnull NSString *)tag {
}

- (void)removeUserAttribute:(nonnull NSString *)key {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"REMOVE_USER_ATTRIBUTE"];
__weak MParticleUser *weakSelf = self;
NSDate *timestamp = [NSDate date];
dispatch_async([MParticle messageQueue], ^{
Expand Down Expand Up @@ -436,6 +441,7 @@ - (void)removeUserAttribute:(nonnull NSString *)key {

#pragma mark - User Segments
- (void)getUserAudiencesWithCompletionHandler:(void (^)(NSArray<MPAudience *> *currentAudiences, NSError * _Nullable error))completionHandler {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"GET_USER_AUDIENCES"];
if ([MParticle sharedInstance].stateMachine.enableAudienceAPI) {
dispatch_async([MParticle messageQueue], ^{
[self.backendController fetchAudiencesWithCompletionHandler:completionHandler];
Expand All @@ -451,7 +457,8 @@ - (void)getUserAudiencesWithCompletionHandler:(void (^)(NSArray<MPAudience *> *c
#pragma mark - Consent State

- (void)setConsentState:(MPConsentState *)state {

[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"SET_CONSENT_STATE"];

[MPPersistenceController_PRIVATE setConsentState:state forMpid:self.userId];

NSArray<NSDictionary *> *kitConfig = [[MParticle sharedInstance].kitContainer_PRIVATE.originalConfig copy];
Expand All @@ -474,6 +481,7 @@ - (void)setConsentState:(MPConsentState *)state {
}

- (nullable MPConsentState *)consentState {
[[MParticle sharedInstance].rokt logRoktApiDiagnostic:@"GET_CONSENT_STATE"];
return [MPPersistenceController_PRIVATE consentStateForMpid:self.userId];
}

Expand Down
4 changes: 4 additions & 0 deletions mParticle-Apple-SDK/Include/MPKitProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@
filteredUser:(FilteredMParticleUser * _Nonnull)filteredUser;
- (BOOL)handleURLCallback:(NSURL * _Nonnull)url;

/// Forward a bounded, non-PII public-API-usage diagnostic code (e.g. "LOG_EVENT") to the kit.
/// Implemented by the Rokt kit only; records which public mParticle API a partner called.
- (void)logMParticleApiDiagnostic:(NSString * _Nonnull)code;

@end

#endif
17 changes: 17 additions & 0 deletions mParticle-Apple-SDK/MPRokt+MParticlePrivate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// MPRokt+MParticlePrivate.h
// mParticle-Apple-SDK
//
// Internal forwarder used by mParticle core to record public-API usage into the Rokt kit.
// Not partner-facing — deliberately kept out of the public MPRokt.h.
//

#import "MPRokt.h"

@interface MPRokt (MParticlePrivate)

/// Forwards a bounded, non-PII public-API-usage diagnostic code (uppercase SNAKE_CASE, e.g.
/// "LOG_EVENT") to the Rokt kit — only when the kit is active, no-op otherwise.
- (void)logRoktApiDiagnostic:(NSString * _Nonnull)code;

@end
Loading
Loading