Skip to content

Commit 2d14531

Browse files
committed
feat(expo): add shared custom base URL support
Add a shared Expo customBaseUrl option for CNAME setup so iOS and Android use the same configuration value during mParticle startup. Update Android integration requirements to 5.79.0 and align the sample and documentation with the new shared setting. #agentic
1 parent a21ca54 commit 2d14531

11 files changed

Lines changed: 133 additions & 77 deletions

File tree

ExpoTestApp/README.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ This app tests the Expo config plugin integration for the mParticle React Native
4242
"androidApiSecret": "YOUR_ANDROID_API_SECRET",
4343
"logLevel": "verbose",
4444
"environment": "development",
45-
"iosCustomBaseURL": "https://cname.example.com",
4645
"iosKits": ["mParticle-Rokt"],
4746
"androidKits": ["android-rokt-kit"]
4847
}
@@ -100,7 +99,9 @@ The Rokt section also demonstrates:
10099
- Using `RoktLayoutView` as an embedded placeholder component
101100

102101
On Android, the Rokt session APIs require `android-core` and
103-
`android-rokt-kit` `5.77.0` or newer.
102+
`android-rokt-kit` `5.79.0` or newer. If configured, the shared Expo
103+
`customBaseUrl` setting is applied to Android through
104+
`NetworkOptions.setCustomBaseURL`.
104105

105106
### Implementation guide: Shoppable Ads (`selectShoppableAds`)
106107

@@ -156,9 +157,6 @@ Check `ios/MParticleExpoTest/AppDelegate.swift` for:
156157
mParticleOptions.environment = .development
157158
let identifyRequest = MPIdentityApiRequest.withEmptyUser()
158159
mParticleOptions.identifyRequest = identifyRequest
159-
let networkOptions = MPNetworkOptions()
160-
networkOptions.customBaseURL = URL(string: "https://cname.example.com")
161-
mParticleOptions.networkOptions = networkOptions
162160
MParticle.sharedInstance().start(with: mParticleOptions)
163161
```
164162

@@ -181,9 +179,6 @@ For older Expo SDK versions, check `ios/MParticleExpoTest/AppDelegate.mm` for:
181179
mParticleOptions.environment = MPEnvironmentDevelopment;
182180
MPIdentityApiRequest *identifyRequest = [MPIdentityApiRequest requestWithEmptyUser];
183181
mParticleOptions.identifyRequest = identifyRequest;
184-
MPNetworkOptions *networkOptions = [[MPNetworkOptions alloc] init];
185-
networkOptions.customBaseURL = [NSURL URLWithString:@"https://cname.example.com"];
186-
mParticleOptions.networkOptions = networkOptions;
187182
[[MParticle sharedInstance] startWithOptions:mParticleOptions];
188183
```
189184
@@ -285,5 +280,5 @@ dependencies {
285280
| `dataPlanId` | string | Data plan ID for validation |
286281
| `dataPlanVersion` | number | Data plan version |
287282
| `iosKits` | string[] | iOS kit pod names (e.g., `["mParticle-Rokt"]`) |
288-
| `iosCustomBaseURL` | string | iOS custom base URL for global CNAME setup |
283+
| `customBaseUrl` | string | Custom base URL for global CNAME setup on iOS and Android |
289284
| `androidKits` | string[] | Android kit dependencies (e.g., `["android-rokt-kit"]`) |

ExpoTestApp/app.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"logLevel": "verbose",
4646
"useEmptyIdentifyRequest": true,
4747
"environment": "development",
48-
"iosCustomBaseURL": "https://cname.example.com",
4948
"iosKits": ["mParticle-Rokt"],
5049
"androidKits": ["android-rokt-kit"]
5150
}

MIGRATING.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ MParticle.Rokt.getSessionId(): Promise<string | null>
3232

3333
`close()` is supported on iOS and Android. Session APIs are backed by the iOS
3434
mParticle Rokt kit. On Android, apps that use these session APIs must use
35-
`android-core` and `android-rokt-kit` `5.77.0` or newer.
35+
`android-core` and `android-rokt-kit` `5.79.0` or newer.
3636

3737
### Expo Config Plugin
3838

@@ -52,17 +52,18 @@ Use `iosKits: ["mParticle-Rokt"]` for standard Rokt placements:
5252
The plugin pins generated `mParticle-Rokt` pods to `~> 9.2`. It does not add
5353
payment-extension pods or URL callback forwarding in this release.
5454

55-
For global CNAME setup, configure `iosCustomBaseURL`:
55+
For global CNAME setup, configure the shared `customBaseUrl` setting:
5656

5757
```json
5858
{
59-
"iosCustomBaseURL": "https://cname.example.com"
59+
"customBaseUrl": "https://cname.example.com"
6060
}
6161
```
6262

63-
The plugin applies this through `MPNetworkOptions.customBaseURL` before
64-
mParticle starts. There is no runtime JavaScript setter because the Rokt kit
65-
reads this setting during initialization.
63+
The plugin applies this through `MPNetworkOptions.customBaseURL` on iOS and
64+
`NetworkOptions.setCustomBaseURL` on Android before mParticle starts. There is
65+
no runtime JavaScript setter because the Rokt kit reads this setting during
66+
initialization.
6667

6768
### Notes
6869

README.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ npx expo run:android
8282
| `dataPlanId` | string | No | Data plan ID for validation |
8383
| `dataPlanVersion` | number | No | Data plan version |
8484
| `iosKits` | string[] | No | iOS kit pod names (e.g., `['mParticle-Rokt']`) |
85-
| `iosCustomBaseURL` | string | No | iOS custom base URL for global CNAME setup |
85+
| `customBaseUrl` | string | No | Custom base URL for global CNAME setup on iOS and Android |
8686
| `androidKits` | string[] | No | Android kit artifact names (e.g., `['android-rokt-kit']`) |
8787
| `useEmptyIdentifyRequest` | boolean | No | Use empty user identify request at init (default: `true`) |
8888

@@ -101,7 +101,6 @@ npx expo run:android
101101
"androidApiSecret": "YOUR_ANDROID_API_SECRET",
102102
"environment": "development",
103103
"logLevel": "verbose",
104-
"iosCustomBaseURL": "https://cname.example.com",
105104
"iosKits": ["mParticle-Rokt", "mParticle-Amplitude"],
106105
"androidKits": ["android-rokt-kit", "android-amplitude-kit"]
107106
}
@@ -111,18 +110,27 @@ npx expo run:android
111110
}
112111
```
113112

113+
For global CNAME setup, add the optional shared `customBaseUrl` setting:
114+
115+
```json
116+
{
117+
"customBaseUrl": "https://cname.example.com"
118+
}
119+
```
120+
114121
### What the Plugin Does
115122

116123
**iOS:**
117124

118125
- Adds mParticle SDK initialization to `AppDelegate` (supports both Swift and Objective-C)
119-
- Sets `MPNetworkOptions.customBaseURL` before startup when `iosCustomBaseURL` is configured
126+
- Sets `MPNetworkOptions.customBaseURL` before startup when `customBaseUrl` is configured
120127
- Configures `pre_install` hook in Podfile for dynamic framework linking
121128
- Adds specified kit pod dependencies
122129

123130
**Android:**
124131

125132
- Adds mParticle SDK initialization to `MainApplication` (supports both Kotlin and Java)
133+
- Sets `NetworkOptions.setCustomBaseURL` before startup when `customBaseUrl` is configured
126134
- Adds specified kit Maven dependencies to `build.gradle`
127135

128136
### Version Support
@@ -294,7 +302,8 @@ See [MIGRATING.md](./MIGRATING.md) for release-specific migration guidance.
294302
295303
For Android integrations that use `MParticle.Rokt.setSessionId()` or
296304
`MParticle.Rokt.getSessionId()`, `android-core` and `android-rokt-kit`
297-
`5.77.0` or newer are required.
305+
`5.79.0` or newer are required. Android CNAME setup through
306+
`customBaseUrl` also requires `android-core` `5.79.0` or newer.
298307
299308
See [Identity](http://docs.mparticle.com/developers/sdk/ios/identity/) for more information on supplying an `MPIdentityApiRequest` object during SDK initialization.
300309
@@ -315,20 +324,28 @@ and build your workspace from xCode.
315324
For more help, see [the Android set up docs](https://docs.mparticle.com/developers/sdk/android/getting-started/#create-an-input).
316325
317326
```kotlin
318-
package com.example.myapp;
327+
package com.example.myapp
319328
320-
import android.app.Application;
321-
import com.mparticle.MParticle;
329+
import android.app.Application
330+
import com.mparticle.MParticle
331+
import com.mparticle.MParticleOptions
332+
import com.mparticle.networking.NetworkOptions
322333
323334
class MyApplication : Application() {
324-
fun onCreate() {
335+
override fun onCreate() {
325336
super.onCreate()
326337
val options: MParticleOptions = MParticleOptions.builder(this)
327338
.credentials("REPLACE ME WITH KEY", "REPLACE ME WITH SECRET")
328339
//optional
329340
.logLevel(MParticle.LogLevel.VERBOSE)
330341
//optional
331342
.identify(identifyRequest)
343+
//optional global CNAME setup
344+
.networkOptions(
345+
NetworkOptions.builder()
346+
.setCustomBaseURL("https://cname.example.com")
347+
.build()
348+
)
332349
//optional
333350
.identifyTask(
334351
BaseIdentityTask()

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ dependencies {
118118
//
119119
// (See https://github.com/mparticle/mparticle-android-sdk for the latest version)
120120
//
121-
api 'com.mparticle:android-core:[5.9.3, )'
121+
api 'com.mparticle:android-core:[5.79.0, )'
122122

123123
//
124124
// And, if you want to include kits, you can do so as follows:
@@ -134,6 +134,6 @@ dependencies {
134134
testImplementation 'junit:junit:4.13.2'
135135
testImplementation files('libs/java-json.jar')
136136

137-
testImplementation 'com.mparticle:android-core:5+'
137+
testImplementation 'com.mparticle:android-core:5.79.0'
138138
testImplementation("com.facebook.react:react-android:+")
139139
}

plugin/src/customBaseUrl.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export interface CustomBaseUrlProps {
2+
customBaseUrl?: string;
3+
}
4+
5+
export function getCustomBaseUrl(props: CustomBaseUrlProps): string | null {
6+
const customBaseUrl = props.customBaseUrl?.trim();
7+
8+
if (!customBaseUrl) {
9+
return null;
10+
}
11+
12+
let parsedCustomBaseUrl: URL;
13+
try {
14+
parsedCustomBaseUrl = new URL(customBaseUrl);
15+
} catch {
16+
throw new Error(
17+
'react-native-mparticle customBaseUrl must be a valid https URL'
18+
);
19+
}
20+
21+
if (
22+
parsedCustomBaseUrl.protocol !== 'https:' ||
23+
!parsedCustomBaseUrl.hostname
24+
) {
25+
throw new Error(
26+
'react-native-mparticle customBaseUrl must be a valid https URL'
27+
);
28+
}
29+
30+
return customBaseUrl;
31+
}

plugin/src/withMParticle.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ export interface MParticlePluginProps {
5757
iosKits?: string[];
5858

5959
/**
60-
* iOS custom base URL for global CNAME setup.
61-
* This is applied before mParticle starts.
60+
* Custom base URL for global CNAME setup.
61+
* This is applied before mParticle starts on iOS and Android.
6262
* @example 'https://your-cname.example.com'
6363
*/
64-
iosCustomBaseURL?: string;
64+
customBaseUrl?: string;
6565

6666
/**
6767
* Android kit artifact names to include (version auto-detected from core SDK)

plugin/src/withMParticleAndroid.ts

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from '@expo/config-plugins';
66
import { mergeContents } from '@expo/config-plugins/build/utils/generateCode';
77
import { MParticlePluginProps } from './withMParticle';
8+
import { getCustomBaseUrl } from './customBaseUrl';
89

910
// Tag used for mergeContents to identify code blocks added by this plugin
1011
const MPARTICLE_TAG = 'react-native-mparticle';
@@ -62,6 +63,7 @@ function generateKotlinInitCode(props: MParticlePluginProps): string {
6263
dataPlanId,
6364
dataPlanVersion,
6465
} = props;
66+
const customBaseUrl = getCustomBaseUrl(props);
6567

6668
const lines: string[] = [
6769
'// mParticle SDK initialization',
@@ -84,6 +86,16 @@ function generateKotlinInitCode(props: MParticlePluginProps): string {
8486
lines.push(` .dataplan("${dataPlanId}"${versionParam})`);
8587
}
8688

89+
if (customBaseUrl) {
90+
lines.push(' .networkOptions(');
91+
lines.push(' NetworkOptions.builder()');
92+
lines.push(
93+
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
94+
);
95+
lines.push(' .build()');
96+
lines.push(' )');
97+
}
98+
8799
if (useEmptyIdentifyRequest) {
88100
lines.push(' .identify(IdentityApiRequest.withEmptyUser().build())');
89101
}
@@ -107,6 +119,7 @@ function generateJavaInitCode(props: MParticlePluginProps): string {
107119
dataPlanId,
108120
dataPlanVersion,
109121
} = props;
122+
const customBaseUrl = getCustomBaseUrl(props);
110123

111124
const lines: string[] = [
112125
'// mParticle SDK initialization',
@@ -129,6 +142,16 @@ function generateJavaInitCode(props: MParticlePluginProps): string {
129142
lines.push(` .dataplan("${dataPlanId}"${versionParam})`);
130143
}
131144

145+
if (customBaseUrl) {
146+
lines.push(' .networkOptions(');
147+
lines.push(' NetworkOptions.builder()');
148+
lines.push(
149+
` .setCustomBaseURL(${JSON.stringify(customBaseUrl)})`
150+
);
151+
lines.push(' .build()');
152+
lines.push(' )');
153+
}
154+
132155
if (useEmptyIdentifyRequest) {
133156
lines.push(' .identify(IdentityApiRequest.withEmptyUser().build())');
134157
}
@@ -143,19 +166,35 @@ function generateJavaInitCode(props: MParticlePluginProps): string {
143166
/**
144167
* Generate mParticle import statements for Kotlin
145168
*/
146-
function getKotlinImports(): string {
147-
return `import com.mparticle.MParticle
148-
import com.mparticle.MParticleOptions
149-
import com.mparticle.identity.IdentityApiRequest`;
169+
function getKotlinImports(props: MParticlePluginProps): string {
170+
const imports = [
171+
'import com.mparticle.MParticle',
172+
'import com.mparticle.MParticleOptions',
173+
'import com.mparticle.identity.IdentityApiRequest',
174+
];
175+
176+
if (getCustomBaseUrl(props)) {
177+
imports.push('import com.mparticle.networking.NetworkOptions');
178+
}
179+
180+
return imports.join('\n');
150181
}
151182

152183
/**
153184
* Generate mParticle import statements for Java
154185
*/
155-
function getJavaImports(): string {
156-
return `import com.mparticle.MParticle;
157-
import com.mparticle.MParticleOptions;
158-
import com.mparticle.identity.IdentityApiRequest;`;
186+
function getJavaImports(props: MParticlePluginProps): string {
187+
const imports = [
188+
'import com.mparticle.MParticle;',
189+
'import com.mparticle.MParticleOptions;',
190+
'import com.mparticle.identity.IdentityApiRequest;',
191+
];
192+
193+
if (getCustomBaseUrl(props)) {
194+
imports.push('import com.mparticle.networking.NetworkOptions;');
195+
}
196+
197+
return imports.join('\n');
159198
}
160199

161200
/**
@@ -210,7 +249,7 @@ function addMParticleToKotlinMainApplication(
210249
// Add import statements using mergeContents
211250
const withImports = mergeContents({
212251
src: contents,
213-
newSrc: getKotlinImports(),
252+
newSrc: getKotlinImports(props),
214253
anchor: /^package .+$/m,
215254
offset: 1, // Add after package declaration
216255
tag: `${MPARTICLE_TAG}-import`,
@@ -261,7 +300,7 @@ function addMParticleToJavaMainApplication(
261300
// Add import statements using mergeContents
262301
const withImports = mergeContents({
263302
src: contents,
264-
newSrc: getJavaImports(),
303+
newSrc: getJavaImports(props),
265304
anchor: /^package .+;$/m,
266305
offset: 1, // Add after package declaration
267306
tag: `${MPARTICLE_TAG}-import`,

0 commit comments

Comments
 (0)