Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
5 changes: 4 additions & 1 deletion .github/workflows/build-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ jobs:

- name: Run static analyzer
run: |
bash -c '! (set -o pipefail && xcodebuild -project "mParticle-Apple-SDK.xcodeproj" -scheme "mParticle-Apple-SDK" -sdk iphonesimulator -configuration Debug -destination "platform=iOS Simulator,name=iPhone 16 Pro,OS=latest" clean analyze | grep -v "warning: The iOS Simulator deployment target" | grep -v "warning: Metadata extraction skipped" | grep -F -v "Repeated use of MParticle sharedInstance in one method; assign to a local variable once." | grep -F -v "warning: Run script build phase '"'"'Run Script'"'"' will be run during every build" | grep -B3 "warning")'
# Fail if any *diagnostic* warnings remain after allowlist filters.
# Use ': warning:' (clang/Xcode style) — not bare 'warning', which false-positives on
# swiftc flags like -suppress-warnings in Swift package builds (e.g. RoktContracts).
bash -c '! (set -o pipefail && xcodebuild -project "mParticle-Apple-SDK.xcodeproj" -scheme "mParticle-Apple-SDK" -sdk iphonesimulator -configuration Debug -destination "platform=iOS Simulator,name=iPhone 16 Pro,OS=latest" clean analyze | grep -v "warning: The iOS Simulator deployment target" | grep -v "warning: Metadata extraction skipped" | grep -F -v "Repeated use of MParticle sharedInstance in one method; assign to a local variable once." | grep -F -v "warning: Run script build phase '"'"'Run Script'"'"' will be run during every build" | grep -B3 ": warning:")'
15 changes: 13 additions & 2 deletions Example/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ target 'mParticleExample' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
use_frameworks!

# Pods for mParticleExample
# Local monorepo SDK (same checkout as this Example/ directory)
pod 'mParticle-Apple-SDK-Swift', :path => '../mParticle-Apple-SDK-Swift'
pod 'mParticle-Apple-SDK', :path => '..'

pod 'mParticle-Apple-Media-SDK/mParticleMediaNoLocation', '~> 1.7.0'
pod 'mParticle-Rokt', '~> 8.0'

# Rokt-Widget pulls in RoktUXHelper (~> 0.8) transitively. This explicit line is only to resolve it
# from Git (tag) instead of CocoaPods trunk when CDN/trunk is unreliable — remove it if trunk works.
pod 'RoktUXHelper', :git => 'https://github.com/ROKT/rokt-ux-helper-ios.git', :tag => '0.8.3'

# Rokt iOS SDK 5.x (aligned with Kits/rokt/rokt/Package.swift). Use Git branch; not on CocoaPods trunk.
pod 'Rokt-Widget', :git => 'https://github.com/ROKT/rokt-sdk-ios.git', :branch => 'workstation/5.0.0'

# Local Rokt kit — uses the same local mParticle-Apple-SDK above (single resolved copy via :path).
# RoktContracts resolves from CocoaPods trunk per podspecs (~> 0.1).
pod 'mParticle-Rokt', :path => '../Kits/rokt/rokt'
#pod 'PLCrashReporter', '~> 1.11.1'
#pod 'mParticle-UrbanAirship', :path => '../../mparticle-apple-integration-urbanairship'
#pod 'mParticle-BranchMetrics', :path => '../../mparticle-apple-integration-branchmetrics'
Expand Down
119 changes: 94 additions & 25 deletions Example/mParticleExample/ViewController.m
Original file line number Diff line number Diff line change
@@ -1,12 +1,76 @@
#import "ViewController.h"
#import <mParticle_Apple_SDK/mParticle.h>
@import RoktContracts;
#import <mParticle_Apple_Media_SDK-Swift.h>
#import <AdSupport/AdSupport.h>
#import "AdSupport/ASIdentifierManager.h"
#if TARGET_OS_IOS == 1 && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#endif

/// Reference / Swift example: `logRoktContractsExampleEvent` — logs RoktContracts events for debugging.
static NSString *MPE_RoktExampleStringOrUnknown(NSString * _Nullable value) {
return value.length > 0 ? value : @"unknown";
}

static void MPE_LogRoktContractsExampleEvent(RoktEvent *event) {
if ([event isKindOfClass:[RoktInitComplete class]]) {
RoktInitComplete *e = (RoktInitComplete *)event;
NSLog(@"Rokt Init Complete - Success: %@", e.success ? @"YES" : @"NO");
} else if ([event isKindOfClass:[RoktShowLoadingIndicator class]]) {
NSLog(@"Rokt: Show Loading Indicator");
} else if ([event isKindOfClass:[RoktHideLoadingIndicator class]]) {
NSLog(@"Rokt: Hide Loading Indicator");
} else if ([event isKindOfClass:[RoktPlacementReady class]]) {
RoktPlacementReady *e = (RoktPlacementReady *)event;
NSLog(@"Rokt Placement Ready - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktPlacementInteractive class]]) {
RoktPlacementInteractive *e = (RoktPlacementInteractive *)event;
NSLog(@"Rokt Placement Interactive - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktOfferEngagement class]]) {
RoktOfferEngagement *e = (RoktOfferEngagement *)event;
NSLog(@"Rokt Offer Engagement - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktPositiveEngagement class]]) {
RoktPositiveEngagement *e = (RoktPositiveEngagement *)event;
NSLog(@"Rokt Positive Engagement - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktFirstPositiveEngagement class]]) {
RoktFirstPositiveEngagement *e = (RoktFirstPositiveEngagement *)event;
NSLog(@"Rokt First Positive Engagement - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktOpenUrl class]]) {
RoktOpenUrl *e = (RoktOpenUrl *)event;
NSLog(
@"Rokt Open URL - ID: %@, URL: %@",
MPE_RoktExampleStringOrUnknown(e.identifier),
e.url
);
} else if ([event isKindOfClass:[RoktPlacementClosed class]]) {
RoktPlacementClosed *e = (RoktPlacementClosed *)event;
NSLog(@"Rokt Placement Closed - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktPlacementCompleted class]]) {
RoktPlacementCompleted *e = (RoktPlacementCompleted *)event;
NSLog(@"Rokt Placement Completed - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktPlacementFailure class]]) {
RoktPlacementFailure *e = (RoktPlacementFailure *)event;
NSLog(@"Rokt Placement Failure - ID: %@", MPE_RoktExampleStringOrUnknown(e.identifier));
} else if ([event isKindOfClass:[RoktEmbeddedSizeChanged class]]) {
RoktEmbeddedSizeChanged *e = (RoktEmbeddedSizeChanged *)event;
NSLog(@"Rokt Embedded Size Changed - ID: %@, height: %@", e.identifier, @(e.updatedHeight));
} else if ([event isKindOfClass:[RoktCartItemInstantPurchase class]]) {
RoktCartItemInstantPurchase *e = (RoktCartItemInstantPurchase *)event;
NSLog(@"Rokt Cart Item Instant Purchase:");
NSLog(@" - Placement ID: %@", e.identifier);
NSLog(@" - Catalog Item ID: %@", e.catalogItemId);
NSLog(@" - Cart Item ID: %@", e.cartItemId);
NSLog(@" - Name: %@", MPE_RoktExampleStringOrUnknown(e.name));
NSLog(@" - Currency: %@", e.currency);
NSLog(@" - Unit Price: %@", e.unitPrice ?: @0);
NSLog(@" - Total Price: %@", e.totalPrice ?: @0);
NSLog(@" - Quantity: %@", e.quantity ?: @0);
} else {
NSLog(@"Rokt: Unknown event type - %@", NSStringFromClass([event class]));
}
}


@interface ViewController () <UITableViewDataSource, UITableViewDelegate>

Expand All @@ -16,7 +80,7 @@ @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UITextField *emailField;
@property (nonatomic, strong) UITextField *customerIDField;
@property (nonatomic, strong) MPRoktEmbeddedView *roktView;
@property (nonatomic, strong) RoktEmbeddedView *roktView;

@end

Expand Down Expand Up @@ -58,7 +122,7 @@ - (void)setupUI:(CGFloat)roktHeight {
if (self.roktView) {
self.roktView.frame = roktFrame;
} else {
self.roktView = [[MPRoktEmbeddedView alloc] initWithFrame:roktFrame];
self.roktView = [[RoktEmbeddedView alloc] initWithFrame:roktFrame];
}
[self.view addSubview:_roktView];

Expand Down Expand Up @@ -254,13 +318,14 @@ - (void)selectDarkOverlayPlacement {
@"mobile": @"(555)867-5309"
};

MPRoktConfig *roktConfig = [[MPRoktConfig alloc] init];
roktConfig.colorMode = MPColorModeDark;
RoktConfig *roktConfig = [[[[RoktConfigBuilder alloc] init] colorMode:RoktColorModeDark] build];
[[MParticle sharedInstance].rokt selectPlacements:@"RoktLayout"
attributes:customAttributes
embeddedViews:nil
config:roktConfig
callbacks:nil];
onEvent:^(RoktEvent *_Nonnull event) {
MPE_LogRoktContractsExampleEvent(event);
}];
}

- (void)selectEmbeddedPlacement {
Expand All @@ -271,27 +336,31 @@ - (void)selectEmbeddedPlacement {
@"sandbox": @"true",
@"mobile": @"(555)867-5309"
};

MPRoktEventCallback *callbacks = [[MPRoktEventCallback alloc] init];
callbacks.onLoad = ^{
// Optional callback for when the Rokt placement loads
};
callbacks.onUnLoad = ^{
// Optional callback for when the Rokt placement unloads
};
callbacks.onShouldShowLoadingIndicator = ^{
// Optional callback to show a loading indicator
};
callbacks.onShouldHideLoadingIndicator = ^{
// Optional callback to hide a loading indicator
};
callbacks.onEmbeddedSizeChange = ^(NSString *placement, CGFloat size) {
[self setupUI:size];
};

NSDictionary *embeddedViews = @{@"Location1": self.roktView};

[[MParticle sharedInstance].rokt selectPlacements:@"testiOS" attributes:customAttributes embeddedViews:embeddedViews config:nil callbacks:callbacks];
if (CGRectGetHeight(self.roktView.frame) < 1.0) {
[self setupUI:320];
}

NSDictionary *embeddedViews = @{@"Location1": self.roktView};
__weak typeof(self) weakSelf = self;

[[MParticle sharedInstance].rokt selectPlacements:@"testiOS"
attributes:customAttributes
embeddedViews:embeddedViews
config:nil
onEvent:^(RoktEvent *_Nonnull event) {
MPE_LogRoktContractsExampleEvent(event);
if ([event isKindOfClass:[RoktEmbeddedSizeChanged class]]) {
RoktEmbeddedSizeChanged *sizeEvent = (RoktEmbeddedSizeChanged *)event;
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[strongSelf setupUI:sizeEvent.updatedHeight];
});
}
}];
}

- (void)selectOverlayPlacementAutoClose {
Expand Down
20 changes: 15 additions & 5 deletions Kits/rokt/rokt/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ let package = Package(
)
],
dependencies: [
// To build the Rokt kit against the monorepo SDK with `SwiftExample` (single resolved
// `mParticle-Apple-SDK`), comment out the block below and uncomment `.package(path:)`.
// .package(path: "../../../"),
.package(
url: "https://github.com/mParticle/mparticle-apple-sdk",
branch: "workstation/9.0-Release"
Expand All @@ -20,6 +23,10 @@ let package = Package(
url: "https://github.com/ROKT/rokt-sdk-ios",
branch: "workstation/5.0.0"
),
.package(
url: "https://github.com/ROKT/rokt-contracts-apple.git",
.upToNextMajor(from: "0.1.0")
),
.package(
url: "https://github.com/erikdoe/ocmock",
branch: "master"
Expand All @@ -29,8 +36,9 @@ let package = Package(
.target(
name: "mParticle-Rokt",
dependencies: [
.product(name: "mParticle-Apple-SDK", package: "mParticle-Apple-SDK"),
.product(name: "Rokt-Widget", package: "rokt-sdk-ios")
.product(name: "mParticle-Apple-SDK", package: "mparticle-apple-sdk"),
.product(name: "Rokt-Widget", package: "rokt-sdk-ios"),
.product(name: "RoktContracts", package: "rokt-contracts-apple")
],
path: "Sources/mParticle-Rokt",
resources: [.process("PrivacyInfo.xcprivacy")],
Expand All @@ -40,8 +48,9 @@ let package = Package(
name: "mParticle-Rokt-Swift",
dependencies: [
"mParticle-Rokt",
.product(name: "mParticle-Apple-SDK", package: "mParticle-Apple-SDK"),
.product(name: "Rokt-Widget", package: "rokt-sdk-ios")
.product(name: "mParticle-Apple-SDK", package: "mparticle-apple-sdk"),
.product(name: "Rokt-Widget", package: "rokt-sdk-ios"),
.product(name: "RoktContracts", package: "rokt-contracts-apple")
],
path: "Sources/mParticle-Rokt-Swift"
),
Expand All @@ -56,7 +65,8 @@ let package = Package(
name: "mParticle-RoktSwiftTests",
dependencies: [
"mParticle-Rokt",
"mParticle-Rokt-Swift"
"mParticle-Rokt-Swift",
.product(name: "RoktContracts", package: "rokt-contracts-apple")
]
)
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation
import ObjectiveC
import mParticle_Apple_SDK
import RoktContracts

// Swift omits `events:onEvent:` and the full `selectPlacements:…onEvent:` from generated Swift API (RoktEvent block bridging).
extension MPRokt {
public func subscribeToPlacementEvents(
_ identifier: String,
onEvent: @escaping (RoktEvent) -> Void
) {
typealias Block = @convention(block) (RoktEvent) -> Void
let sel = NSSelectorFromString("events:onEvent:")
guard let method = class_getInstanceMethod(MPRokt.self, sel) else { return }
let imp = method_getImplementation(method)
typealias Fn = @convention(c) (AnyObject, Selector, NSString, Block?) -> Void
unsafeBitCast(imp, to: Fn.self)(self, sel, identifier as NSString, onEvent as Block)
}

public func subscribeToGlobalEvents(_ onEvent: @escaping (RoktEvent) -> Void) {
typealias Block = @convention(block) (RoktEvent) -> Void
let sel = NSSelectorFromString("globalEvents:")
guard let method = class_getInstanceMethod(MPRokt.self, sel) else { return }
let imp = method_getImplementation(method)
typealias Fn = @convention(c) (AnyObject, Selector, Block) -> Void
unsafeBitCast(imp, to: Fn.self)(self, sel, onEvent as Block)
}

public func selectPlacements(
_ identifier: String,
attributes: [String: String],
embeddedViews: [String: RoktEmbeddedView]?,
config: RoktConfig?,
onEvent: ((RoktEvent) -> Void)?
) {
typealias EventBlock = @convention(block) (RoktEvent) -> Void
let sel = NSSelectorFromString("selectPlacements:attributes:embeddedViews:config:onEvent:")
guard let method = class_getInstanceMethod(MPRokt.self, sel) else { return }
let imp = method_getImplementation(method)
typealias Fn = @convention(c) (
AnyObject,
Selector,
NSString,
NSDictionary?,
NSDictionary?,
RoktConfig?,
EventBlock?
) -> Void
let attrs = attributes as NSDictionary
let embedded = embeddedViews as NSDictionary?
let block: EventBlock? = onEvent.map { cb in cb as EventBlock }
unsafeBitCast(imp, to: Fn.self)(self, sel, identifier as NSString, attrs, embedded, config, block)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import SwiftUI
import Rokt_Widget
import RoktContracts
import mParticle_Apple_SDK
import mParticle_Rokt

Expand Down
Loading
Loading