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
4 changes: 3 additions & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ jobs:
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1.7.0
with:
xcode-version: latest-stable
# Avoid `latest-stable` (Xcode 17+ / Swift 6 defaults) breaking RoktUXHelper
# source builds until Rokt pins are updated upstream.
xcode-version: '^16.0.0'

- name: Set Xcode Toolchain
run: echo "TOOLCHAINS=com.apple.dt.toolchain.XcodeDefault" >> $GITHUB_ENV
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Expo config plugin: optional `pinningDisabled` for `MPNetworkOptions` / `NetworkOptions` at SDK startup

### Fixed

- iOS sample CI: pin `Rokt-Widget` to `5.2.0` and `DcuiSchema` to `2.7.0` (avoids `2.8.x` schema floating under Rokt’s `~> 2.6` and breaking `RoktUXHelper` Swift compile); GitHub Actions stays on Xcode 16.x

## [3.1.2] - 2026-05-28

### Fixed
Expand Down
29 changes: 15 additions & 14 deletions ExpoTestApp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,18 @@ dependencies {

## Plugin Configuration Options

| Option | Type | Description |
| ------------------------- | -------- | --------------------------------------------------------- |
| `iosApiKey` | string | mParticle iOS API key |
| `iosApiSecret` | string | mParticle iOS API secret |
| `androidApiKey` | string | mParticle Android API key |
| `androidApiSecret` | string | mParticle Android API secret |
| `logLevel` | string | Log level: `none`, `error`, `warning`, `debug`, `verbose` |
| `environment` | string | Environment: `development`, `production`, `autoDetect` |
| `useEmptyIdentifyRequest` | boolean | Initialize with empty identify request (default: true) |
| `dataPlanId` | string | Data plan ID for validation |
| `dataPlanVersion` | number | Data plan version |
| `iosKits` | string[] | iOS kit pod names (e.g., `["mParticle-Rokt"]`) |
| `customBaseUrl` | string | Custom base URL for global CNAME setup on iOS and Android |
| `androidKits` | string[] | Android kit dependencies (e.g., `["android-rokt-kit"]`) |
| Option | Type | Description |
| ------------------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `iosApiKey` | string | mParticle iOS API key |
| `iosApiSecret` | string | mParticle iOS API secret |
| `androidApiKey` | string | mParticle Android API key |
| `androidApiSecret` | string | mParticle Android API secret |
| `logLevel` | string | Log level: `none`, `error`, `warning`, `debug`, `verbose` |
| `environment` | string | Environment: `development`, `production`, `autoDetect` |
| `useEmptyIdentifyRequest` | boolean | Initialize with empty identify request (default: true) |
| `dataPlanId` | string | Data plan ID for validation |
| `dataPlanVersion` | number | Data plan version |
| `iosKits` | string[] | iOS kit pod names (e.g., `["mParticle-Rokt"]`) |
| `customBaseUrl` | string | Custom base URL for global CNAME setup on iOS and Android |
| `pinningDisabled` | boolean | Disable SSL pinning (iOS: `MPNetworkOptions.pinningDisabled`; Android: `setPinningDisabledInDevelopment`) |
| `androidKits` | string[] | Android kit dependencies (e.g., `["android-rokt-kit"]`) |
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ npx expo run:android

### Plugin Configuration Options

| Option | Type | Required | Description |
| ------------------------- | -------- | -------- | ------------------------------------------------------------------- |
| `iosApiKey` | string | Yes | iOS API key from mParticle dashboard |
| `iosApiSecret` | string | Yes | iOS API secret from mParticle dashboard |
| `androidApiKey` | string | Yes | Android API key from mParticle dashboard |
| `androidApiSecret` | string | Yes | Android API secret from mParticle dashboard |
| `logLevel` | string | No | Log level: `'none'`, `'error'`, `'warning'`, `'debug'`, `'verbose'` |
| `environment` | string | No | Environment: `'development'`, `'production'`, `'autoDetect'` |
| `dataPlanId` | string | No | Data plan ID for validation |
| `dataPlanVersion` | number | No | Data plan version |
| `iosKits` | string[] | No | iOS kit pod names (e.g., `['mParticle-Rokt']`) |
| `customBaseUrl` | string | No | Custom base URL for global CNAME setup on iOS and Android |
| `androidKits` | string[] | No | Android kit artifact names (e.g., `['android-rokt-kit']`) |
| `useEmptyIdentifyRequest` | boolean | No | Use empty user identify request at init (default: `true`) |
| Option | Type | Required | Description |
| ------------------------- | -------- | -------- | --------------------------------------------------------------------------------------------- |
| `iosApiKey` | string | Yes | iOS API key from mParticle dashboard |
| `iosApiSecret` | string | Yes | iOS API secret from mParticle dashboard |
| `androidApiKey` | string | Yes | Android API key from mParticle dashboard |
| `androidApiSecret` | string | Yes | Android API secret from mParticle dashboard |
| `logLevel` | string | No | Log level: `'none'`, `'error'`, `'warning'`, `'debug'`, `'verbose'` |
| `environment` | string | No | Environment: `'development'`, `'production'`, `'autoDetect'` |
| `dataPlanId` | string | No | Data plan ID for validation |
| `dataPlanVersion` | number | No | Data plan version |
| `iosKits` | string[] | No | iOS kit pod names (e.g., `['mParticle-Rokt']`) |
| `customBaseUrl` | string | No | Custom base URL for global CNAME setup on iOS and Android |
| `pinningDisabled` | boolean | No | Disable SSL pinning (`MPNetworkOptions` on iOS; `setPinningDisabledInDevelopment` on Android) |
| `androidKits` | string[] | No | Android kit artifact names (e.g., `['android-rokt-kit']`) |
| `useEmptyIdentifyRequest` | boolean | No | Use empty user identify request at init (default: `true`) |

### Example with Kits

Expand Down Expand Up @@ -123,14 +124,14 @@ For global CNAME setup, add the optional shared `customBaseUrl` setting:
**iOS:**

- Adds mParticle SDK initialization to `AppDelegate` (supports both Swift and Objective-C)
- Sets `MPNetworkOptions.customBaseURL` before startup when `customBaseUrl` is configured
- Sets `MPNetworkOptions` (`customBaseURL` and/or `pinningDisabled`) before startup when those plugin options are configured
- Configures `pre_install` hook in Podfile for dynamic framework linking
- Adds specified kit pod dependencies

**Android:**

- Adds mParticle SDK initialization to `MainApplication` (supports both Kotlin and Java)
- Sets `NetworkOptions.setCustomBaseURL` before startup when `customBaseUrl` is configured
- Sets `NetworkOptions` (`setCustomBaseURL` and/or `setPinningDisabledInDevelopment`) before startup when those plugin options are configured
- Adds specified kit Maven dependencies to `build.gradle`

### Version Support
Expand Down
9 changes: 9 additions & 0 deletions plugin/src/withMParticle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ export interface MParticlePluginProps {
*/
customBaseUrl?: string;

/**
* When true, disables SSL certificate pinning for mParticle network traffic.
*
* - **iOS:** Sets `MPNetworkOptions.pinningDisabled` before startup.
* - **Android:** Sets `NetworkOptions.Builder.setPinningDisabledInDevelopment(true)`
* (mParticle's Android API for proxy/debug builds; see Android SDK docs).
*/
pinningDisabled?: boolean;

/**
* Android kit artifact names to include (version auto-detected from core SDK)
* @example ['android-rokt-kit', 'android-amplitude-kit']
Expand Down
36 changes: 26 additions & 10 deletions plugin/src/withMParticleAndroid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { getCustomBaseUrl } from './customBaseUrl';
// Tag used for mergeContents to identify code blocks added by this plugin
const MPARTICLE_TAG = 'react-native-mparticle';

function shouldEmitNetworkOptions(props: MParticlePluginProps): boolean {
return Boolean(getCustomBaseUrl(props) || props.pinningDisabled === true);
}

/**
* Get the mParticle log level string for Android
*/
Expand Down Expand Up @@ -64,6 +68,7 @@ function generateKotlinInitCode(props: MParticlePluginProps): string {
dataPlanVersion,
} = props;
const customBaseUrl = getCustomBaseUrl(props);
const pinningDisabled = props.pinningDisabled === true;

const lines: string[] = [
'// mParticle SDK initialization',
Expand All @@ -86,12 +91,17 @@ function generateKotlinInitCode(props: MParticlePluginProps): string {
lines.push(` .dataplan("${dataPlanId}"${versionParam})`);
}

if (customBaseUrl) {
if (shouldEmitNetworkOptions(props)) {
lines.push(' .networkOptions(');
lines.push(' NetworkOptions.builder()');
lines.push(
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
);
if (customBaseUrl) {
lines.push(
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
);
}
if (pinningDisabled) {
lines.push(' .setPinningDisabledInDevelopment(true)');
}
lines.push(' .build()');
lines.push(' )');
}
Expand Down Expand Up @@ -120,6 +130,7 @@ function generateJavaInitCode(props: MParticlePluginProps): string {
dataPlanVersion,
} = props;
const customBaseUrl = getCustomBaseUrl(props);
const pinningDisabled = props.pinningDisabled === true;

const lines: string[] = [
'// mParticle SDK initialization',
Expand All @@ -142,12 +153,17 @@ function generateJavaInitCode(props: MParticlePluginProps): string {
lines.push(` .dataplan("${dataPlanId}"${versionParam})`);
}

if (customBaseUrl) {
if (shouldEmitNetworkOptions(props)) {
lines.push(' .networkOptions(');
lines.push(' NetworkOptions.builder()');
lines.push(
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
);
if (customBaseUrl) {
lines.push(
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
);
}
if (pinningDisabled) {
lines.push(' .setPinningDisabledInDevelopment(true)');
}
lines.push(' .build()');
lines.push(' )');
}
Expand All @@ -173,7 +189,7 @@ function getKotlinImports(props: MParticlePluginProps): string {
'import com.mparticle.identity.IdentityApiRequest',
];

if (getCustomBaseUrl(props)) {
if (shouldEmitNetworkOptions(props)) {
imports.push('import com.mparticle.networking.NetworkOptions');
}

Expand All @@ -190,7 +206,7 @@ function getJavaImports(props: MParticlePluginProps): string {
'import com.mparticle.identity.IdentityApiRequest;',
];

if (getCustomBaseUrl(props)) {
if (shouldEmitNetworkOptions(props)) {
imports.push('import com.mparticle.networking.NetworkOptions;');
}

Expand Down
36 changes: 24 additions & 12 deletions plugin/src/withMParticleIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,19 @@ function generateSwiftInitCode(props: MParticlePluginProps): string {
}

const customBaseUrl = getCustomBaseUrl(props);
if (customBaseUrl) {
const pinningDisabled = props.pinningDisabled === true;
if (customBaseUrl || pinningDisabled) {
lines.push('let networkOptions = MPNetworkOptions()');
lines.push(
`networkOptions.customBaseURL = URL(string: ${JSON.stringify(
customBaseUrl
)})`
);
if (customBaseUrl) {
lines.push(
`networkOptions.customBaseURL = URL(string: ${JSON.stringify(
customBaseUrl
)})`
);
}
if (pinningDisabled) {
lines.push('networkOptions.pinningDisabled = true');
}
lines.push('mParticleOptions.networkOptions = networkOptions');
}

Expand Down Expand Up @@ -196,15 +202,21 @@ function generateObjcInitCode(props: MParticlePluginProps): string {
}

const customBaseUrl = getCustomBaseUrl(props);
if (customBaseUrl) {
const pinningDisabled = props.pinningDisabled === true;
if (customBaseUrl || pinningDisabled) {
lines.push(
'MPNetworkOptions *networkOptions = [[MPNetworkOptions alloc] init];'
);
lines.push(
`networkOptions.customBaseURL = [NSURL URLWithString:@${JSON.stringify(
customBaseUrl
)}];`
);
if (customBaseUrl) {
lines.push(
`networkOptions.customBaseURL = [NSURL URLWithString:@${JSON.stringify(
customBaseUrl
)}];`
);
}
if (pinningDisabled) {
lines.push('networkOptions.pinningDisabled = YES;');
}
lines.push('mParticleOptions.networkOptions = networkOptions;');
}

Expand Down
6 changes: 1 addition & 5 deletions sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,7 @@ cd sample/ios
pod install
```

The sample Podfile pins the standard Rokt kit with:

```ruby
pod 'mParticle-Rokt', '~> 9.2'
```
The sample Podfile pins `mParticle-Rokt` `~> 9.2`, **`Rokt-Widget` `5.2.0`**, and **`DcuiSchema` `2.7.0`**. Rokt’s pods allow `DcuiSchema` to float within `~> 2.6`; when CocoaPods resolves **2.8.0+**, the schema adds `image` styling that can desync from the `RoktUXHelper` sources in that widget line and break Swift compile (`StyleTransformer` / `BaseStyles`). Bump these pins together when you adopt a newer Rokt iOS stack.

The sample Android app pins `com.mparticle:android-core` and
`com.mparticle:android-rokt-kit` to `5.79.0` so the Rokt session APIs and
Expand Down
6 changes: 6 additions & 0 deletions sample/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ target 'MParticleSample' do
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
pod 'mParticle-Rokt', '~> 9.2'
# Exact Rokt-Widget keeps RoktUXHelper on the same version (Rokt pins them together).
pod 'Rokt-Widget', '5.2.0'
# RoktUXHelper declares `DcuiSchema` `~> 2.6`, which otherwise floats to 2.8.x; that
# schema release adds `image` styling APIs that the shipped RoktUXHelper 5.2.x Swift
# does not pass through yet (CI: StyleTransformer.swift / BaseStyles "missing image").
pod 'DcuiSchema', '2.7.0'

target 'MParticleSampleTests' do
inherit! :complete
Expand Down
Loading