Skip to content

Commit 56b8b16

Browse files
authored
add manual SDK initialization (#336)
* add manual SDK initialization * validate api key * updates * undo version bump - android update will bump it
1 parent 58e312c commit 56b8b16

File tree

13 files changed

+322
-36
lines changed

13 files changed

+322
-36
lines changed

README.md

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
## Installation
4646

4747
```sh
48-
$ npm install @intercom/intercom-react-native
48+
npm install @intercom/intercom-react-native
4949
```
5050

5151
or
@@ -61,7 +61,7 @@ If you're using React Native v0.60 or above, the library will be linked automati
6161
#### Android: Automatic linking with React Native v0.59 and below
6262

6363
```
64-
$ react-native link @intercom/intercom-react-native
64+
react-native link @intercom/intercom-react-native
6565
```
6666

6767
#### Android: Manual linking with React Native v0.59 and below
@@ -81,6 +81,16 @@ implementation project(':intercom-react-native')
8181

8282
#### Android: Setup
8383

84+
You have two options for initializing Intercom:
85+
86+
**Option 1: Native Initialization (Recommended)**
87+
Initialize at app startup in your native code for the best user experience.
88+
89+
**Option 2: JavaScript Initialization**
90+
Initialize manually from JavaScript for more control over timing. If you choose this option, skip the native initialization code below and see the [`initialize` method documentation](#intercomintializeapikey-appid) for implementation details.
91+
92+
**For Native Initialization:**
93+
8494
- Add below lines to `android/app/src/main/java/com/YOUR_APP/app/MainApplication.java` inside `onCreate` method, replacing `apiKey` and `appId` which can be found in your [workspace settings](https://app.intercom.com/a/apps/_/settings/android).
8595

8696
```java
@@ -340,6 +350,16 @@ See [How to manually link IOS Intercom SDK](docs/IOS-MANUAL-LINKING.md)
340350

341351
#### iOS: Setup
342352

353+
You have two options for initializing Intercom:
354+
355+
**Option 1: Native Initialization (Recommended)**
356+
Initialize at app startup in your native code for the best user experience.
357+
358+
**Option 2: JavaScript Initialization**
359+
Initialize manually from JavaScript for more control over timing. If you choose this option, skip the native initialization code below and see the [`initialize` method documentation](#intercomintializeapikey-appid) for implementation details with platform-specific API key handling.
360+
361+
**For Native Initialization:**
362+
343363
- Open `ios/AppDelegate.m` then add below code:
344364

345365
- At the top of file add the following:
@@ -359,7 +379,7 @@ See [How to manually link IOS Intercom SDK](docs/IOS-MANUAL-LINKING.md)
359379
// ...
360380
self.window.rootViewController = rootViewController;
361381

362-
[IntercomModule initialize:@"apiKey" withAppId:@"appId"]; // <-- Add this (Remember to replace strings with your api keys)
382+
[IntercomModule initialize:@"apiKey" withAppId:@"appId"]; // <-- Add this
363383

364384
return YES;
365385
}
@@ -384,7 +404,7 @@ func application(_ application: UIApplication, didFinishLaunchingWithOptions lau
384404
....
385405
}
386406

387-
```
407+
```
388408

389409
#### iOS: Permissions
390410

@@ -518,6 +538,7 @@ The plugin provides props for extra customization. Every time you change the pro
518538
- `androidApiKey` (_string_): Android API Key from Intercom.
519539
- `iosApiKey` (_string_): iOS API Key from Intercom.
520540
- `intercomRegion` (_string_): Region for Intercom `US`, `EU`, `AU`. Optional. Defaults to `US`.
541+
- `useManualInit` (_boolean_): Set to `true` to manually initialize Intercom from JavaScript instead of at app startup. Optional. Defaults to `false`.
521542

522543
```json
523544
{
@@ -537,6 +558,41 @@ The plugin provides props for extra customization. Every time you change the pro
537558
}
538559
```
539560

561+
#### Manual Initialization with Expo
562+
563+
If you want to delay Intercom initialization and manually initialize it from JavaScript, you set the `useManualInit` option to `true`:
564+
565+
```json
566+
{
567+
"expo": {
568+
"plugins": [
569+
[
570+
"@intercom/intercom-react-native",
571+
{
572+
"useManualInit": true
573+
}
574+
]
575+
]
576+
}
577+
}
578+
```
579+
580+
Then initialize Intercom manually in your JavaScript code with the platform-specific API keys:
581+
582+
```javascript
583+
import Intercom from '@intercom/intercom-react-native';
584+
import { Platform } from 'react-native';
585+
586+
// You can find your API keys in your Intercom workspace settings
587+
// https://app.intercom.com/a/apps/<your-app-id>/settings/channels/messenger/install?tab=ios
588+
const apiKey = Platform.select({
589+
ios: 'ios_sdk-abc123',
590+
android: 'android_sdk-abc123',
591+
});
592+
593+
await Intercom.initialize(apiKey, 'abc123');
594+
```
595+
540596
#### Expo: Push notifications
541597

542598
Add the following configurations into your `app.json` or `app.config.js`:
@@ -697,6 +753,38 @@ Sets the user hash necessary for validation when Identity Verification is enable
697753

698754
---
699755

756+
### `Intercom.initialize(apiKey, appId)`
757+
758+
Initialize the Intercom SDK manually. This is useful when you want to delay initialization until after your app has started, or when using Expo with the `useManualInit` plugin option.
759+
760+
**Important:** This method configures the SDK but does NOT validate your credentials with Intercom's servers. Invalid API keys or App IDs will only be detected when you attempt to use Intercom features (e.g., login, show messenger). The method will return `true` if the SDK is successfully configured, regardless of credential validity.
761+
762+
### Options
763+
764+
| Name | Type | Required | Description |
765+
| ------ | ------ | -------- | --------------------------------------- |
766+
| apiKey | string | yes | Your Platform-specific Intercom API key |
767+
| appId | string | yes | Your Intercom App ID |
768+
769+
### Examples
770+
771+
```javascript
772+
import { Platform } from 'react-native';
773+
774+
const apiKey = Platform.select({
775+
ios: 'ios_sdk-abc123',
776+
android: 'android_sdk-xyz789',
777+
});
778+
779+
await Intercom.initialize(apiKey, 'your_app_id');
780+
```
781+
782+
### Returns
783+
784+
`Promise<boolean>`
785+
786+
---
787+
700788
### `Intercom.loginUnidentifiedUser()`
701789

702790
Login a unidentified user.
@@ -1223,9 +1311,9 @@ Data Connectors (e.g., Fin Actions), which use these in `Authorization: Bearer <
12231311
12241312
### Options
12251313
1226-
| Name | Type | Required | Description |
1227-
| ---------- | ------------------------------- | -------- | --------------------------------------------------- |
1228-
| authTokens | `{ [key: string]: string }` | yes | An object with token names as keys and JWT strings as values |
1314+
| Name | Type | Required | Description |
1315+
| ---------- | --------------------------- | -------- | ------------------------------------------------------------ |
1316+
| authTokens | `{ [key: string]: string }` | yes | An object with token names as keys and JWT strings as values |
12291317
12301318
### Example
12311319

android/src/main/java/com/intercom/reactnative/IntercomErrorCodes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class IntercomErrorCodes {
1111
public static final String GET_UNREAD_CONVERSATION = "108";
1212
public static final String SET_USER_JWT = "109";
1313
public static final String SET_AUTH_TOKENS = "110";
14+
public static final String INITIALIZE_ERROR = "111";
1415
public static final String DISPLAY_MESSENGER = "201";
1516
public static final String DISPLAY_MESSENGER_COMPOSER = "202";
1617
public static final String DISPLAY_CONTENT = "203";

android/src/newarch/IntercomModule.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,23 @@ public void onFailure(@NonNull IntercomError intercomError) {
626626
}
627627
}
628628

629+
@ReactMethod
630+
public void initialize(String apiKey, String appId, Promise promise) {
631+
try {
632+
Activity activity = getCurrentActivity();
633+
if (activity != null && activity.getApplication() != null) {
634+
IntercomModule.initialize(activity.getApplication(), apiKey, appId);
635+
promise.resolve(true);
636+
} else {
637+
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, "Activity is null");
638+
}
639+
} catch (Exception err) {
640+
Log.e(NAME, "initialize error:");
641+
Log.e(NAME, err.toString());
642+
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, err.toString());
643+
}
644+
}
645+
629646
@ReactMethod
630647
public void setNeedsStatusBarAppearanceUpdate(Promise promise) {
631648
// iOS-only method, no-op on Android

android/src/oldarch/IntercomModule.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,23 @@ public void onFailure(@NonNull IntercomError intercomError) {
603603
}
604604
}
605605

606+
@ReactMethod
607+
public void initialize(String apiKey, String appId, Promise promise) {
608+
try {
609+
Activity activity = getCurrentActivity();
610+
if (activity != null && activity.getApplication() != null) {
611+
IntercomModule.initialize(activity.getApplication(), apiKey, appId);
612+
promise.resolve(true);
613+
} else {
614+
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, "Activity is null");
615+
}
616+
} catch (Exception err) {
617+
Log.e(NAME, "initialize error:");
618+
Log.e(NAME, err.toString());
619+
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, err.toString());
620+
}
621+
}
622+
606623
public static synchronized void initialize(Application application, String apiKey, String appId) {
607624
String sdkVersion = BuildConfig.INTERCOM_VERSION_NAME;
608625
ReactNativeHeaderInterceptor.setReactNativeVersion(application.getApplicationContext(), sdkVersion);

examples/expo-example/app.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@
3939
[
4040
"@intercom/intercom-react-native",
4141
{
42-
"appId": "YOUR_APP_ID",
43-
"androidApiKey": "android_sdk-YOUR_ANDROID_API_KEY",
44-
"iosApiKey": "ios_sdk-YOUR_IOS_API_KEY"
42+
"useManualInit": true
4543
}
4644
],
4745
"expo-router",

examples/expo-example/app/(tabs)/index.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import React from 'react';
2-
import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
1+
import React, { useEffect, useState } from 'react';
2+
import { SafeAreaView, ScrollView, StatusBar, View, Text } from 'react-native';
33
import * as Notifications from 'expo-notifications';
44

55
import { useIntercom } from '../../hooks/useIntercom';
66
import { useNotifications } from '../../hooks/useNotifications';
7+
import { INTERCOM_CONFIG } from '../../config/intercom.config';
78

89
import Header from '../../components/Header';
10+
import Intercom from '@intercom/intercom-react-native';
911
import AuthenticationSection from '../../components/AuthenticationSection';
1012
import MessagingSection from '../../components/MessagingSection';
1113
import ContentSection from '../../components/ContentSection';
@@ -28,6 +30,34 @@ Notifications.setNotificationHandler({
2830
export default function App() {
2931
const intercom = useIntercom();
3032
const notifications = useNotifications();
33+
const [isInitialized, setIsInitialized] = useState(false);
34+
const [initError, setInitError] = useState<string | null>(null);
35+
36+
useEffect(() => {
37+
async function initializeIntercom() {
38+
try {
39+
console.log('Initializing Intercom...');
40+
if (!INTERCOM_CONFIG.apiKey || !INTERCOM_CONFIG.appId) {
41+
console.error('Intercom API key and app ID are required');
42+
return;
43+
}
44+
if (!isInitialized) {
45+
await Intercom.initialize(
46+
INTERCOM_CONFIG.apiKey,
47+
INTERCOM_CONFIG.appId
48+
);
49+
setIsInitialized(true);
50+
setInitError(null);
51+
console.log('Intercom initialized successfully');
52+
}
53+
} catch (error) {
54+
console.error('Failed to initialize Intercom:', error);
55+
setInitError(error instanceof Error ? error.message : 'Unknown error');
56+
}
57+
}
58+
59+
initializeIntercom();
60+
}, [isInitialized]);
3161

3262
return (
3363
<ErrorBoundary>
@@ -41,6 +71,20 @@ export default function App() {
4171
/>
4272

4373
<ScrollView className="flex-1 px-6 py-4">
74+
<View className="mb-4 p-3 rounded-lg bg-gray-100">
75+
<Text className="text-sm font-semibold text-gray-700">
76+
Intercom Status:{' '}
77+
{initError && (
78+
<Text className="text-red-600">Failed: {initError}</Text>
79+
)}
80+
{isInitialized ? (
81+
<Text className="text-green-600">Initialized</Text>
82+
) : (
83+
<Text className="text-yellow-600">Initializing...</Text>
84+
)}
85+
</Text>
86+
</View>
87+
4488
<AuthenticationSection
4589
loggedUser={intercom.loggedUser}
4690
email={intercom.email}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Platform } from 'react-native';
2+
3+
/**
4+
* Intercom configuration
5+
*
6+
* Replace these values with your actual Intercom credentials.
7+
* You can find your API keys and App ID in your Intercom workspace settings:
8+
* https://app.intercom.com/a/apps/<your-app-id>/settings/channels/messenger/install?tab=ios
9+
*
10+
* Note: iOS and Android require different API keys.
11+
*/
12+
export const INTERCOM_CONFIG = {
13+
appId: '<your-app-id>', // Replace with your Intercom App ID
14+
apiKey: Platform.select({
15+
ios: 'ios_sdk-<your-ios-api-key>', // Replace with your iOS API key
16+
android: 'android_sdk-<your-android-api-key>', // Replace with your Android API key
17+
}) as string,
18+
};

examples/expo-example/pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ios/IntercomModule.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ @implementation IntercomModule
1717
NSString *UNREAD_CONVERSATION_COUNT = @"107";
1818
NSString *SET_USER_JWT = @"109";
1919
NSString *SET_AUTH_TOKENS = @"110";
20+
NSString *INITIALIZE_ERROR = @"111";
2021
NSString *SEND_TOKEN_TO_INTERCOM = @"302";
2122
NSString *FETCH_HELP_CENTER_COLLECTIONS = @"901";
2223
NSString *FETCH_HELP_CENTER_COLLECTION = @"902";
@@ -28,6 +29,19 @@ - (dispatch_queue_t)methodQueue {
2829
return dispatch_get_main_queue();
2930
}
3031

32+
RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
33+
withAppId:(NSString *)appId
34+
resolver:(RCTPromiseResolveBlock)resolve
35+
rejecter:(RCTPromiseRejectBlock)reject) {
36+
@try {
37+
[IntercomModule initialize:apiKey withAppId:appId];
38+
resolve(@(YES));
39+
} @catch (NSException *exception) {
40+
NSLog(@"initialize error: %@", exception.reason);
41+
reject(INITIALIZE_ERROR, @"Failed to initialize Intercom", [self exceptionToError:exception :INITIALIZE_ERROR :@"initialize"]);
42+
}
43+
}
44+
3145
+ (void)initialize:(nonnull NSString *)apiKey withAppId:(nonnull NSString *)appId {
3246
NSString *version = @"0";
3347

src/NativeIntercomSpec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface TurboModuleContent {
2727
}
2828

2929
export interface Spec extends TurboModule {
30+
initialize(apiKey: string, appId: string): Promise<boolean>;
3031
loginUnidentifiedUser(): Promise<boolean>;
3132
loginUserWithUserAttributes(userAttributes: UserAttributes): Promise<boolean>;
3233
logout(): Promise<boolean>;

0 commit comments

Comments
 (0)