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
102 changes: 95 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
## Installation

```sh
$ npm install @intercom/intercom-react-native
npm install @intercom/intercom-react-native
```

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

```
$ react-native link @intercom/intercom-react-native
react-native link @intercom/intercom-react-native
```

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

#### Android: Setup

You have two options for initializing Intercom:

**Option 1: Native Initialization (Recommended)**
Initialize at app startup in your native code for the best user experience.

**Option 2: JavaScript Initialization**
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.

**For Native Initialization:**

- 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).

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

#### iOS: Setup

You have two options for initializing Intercom:

**Option 1: Native Initialization (Recommended)**
Initialize at app startup in your native code for the best user experience.

**Option 2: JavaScript Initialization**
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.

**For Native Initialization:**

- Open `ios/AppDelegate.m` then add below code:

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

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

return YES;
}
Expand All @@ -384,7 +404,7 @@ func application(_ application: UIApplication, didFinishLaunchingWithOptions lau
....
}

```
```

#### iOS: Permissions

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

```json
{
Expand All @@ -537,6 +558,41 @@ The plugin provides props for extra customization. Every time you change the pro
}
```

#### Manual Initialization with Expo

If you want to delay Intercom initialization and manually initialize it from JavaScript, you set the `useManualInit` option to `true`:

```json
{
"expo": {
"plugins": [
[
"@intercom/intercom-react-native",
{
"useManualInit": true
}
]
]
}
}
```

Then initialize Intercom manually in your JavaScript code with the platform-specific API keys:

```javascript
import Intercom from '@intercom/intercom-react-native';
import { Platform } from 'react-native';

// You can find your API keys in your Intercom workspace settings
// https://app.intercom.com/a/apps/<your-app-id>/settings/channels/messenger/install?tab=ios
const apiKey = Platform.select({
ios: 'ios_sdk-abc123',
android: 'android_sdk-abc123',
});

await Intercom.initialize(apiKey, 'abc123');
```

#### Expo: Push notifications

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

---

### `Intercom.initialize(apiKey, appId)`

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.

**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.

### Options

| Name | Type | Required | Description |
| ------ | ------ | -------- | --------------------------------------- |
| apiKey | string | yes | Your Platform-specific Intercom API key |
| appId | string | yes | Your Intercom App ID |

### Examples

```javascript
import { Platform } from 'react-native';

const apiKey = Platform.select({
ios: 'ios_sdk-abc123',
android: 'android_sdk-xyz789',
});

await Intercom.initialize(apiKey, 'your_app_id');
```

### Returns

`Promise<boolean>`

---

### `Intercom.loginUnidentifiedUser()`

Login a unidentified user.
Expand Down Expand Up @@ -1223,9 +1311,9 @@ Data Connectors (e.g., Fin Actions), which use these in `Authorization: Bearer <

### Options

| Name | Type | Required | Description |
| ---------- | ------------------------------- | -------- | --------------------------------------------------- |
| authTokens | `{ [key: string]: string }` | yes | An object with token names as keys and JWT strings as values |
| Name | Type | Required | Description |
| ---------- | --------------------------- | -------- | ------------------------------------------------------------ |
| authTokens | `{ [key: string]: string }` | yes | An object with token names as keys and JWT strings as values |

### Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class IntercomErrorCodes {
public static final String GET_UNREAD_CONVERSATION = "108";
public static final String SET_USER_JWT = "109";
public static final String SET_AUTH_TOKENS = "110";
public static final String INITIALIZE_ERROR = "111";
public static final String DISPLAY_MESSENGER = "201";
public static final String DISPLAY_MESSENGER_COMPOSER = "202";
public static final String DISPLAY_CONTENT = "203";
Expand Down
17 changes: 17 additions & 0 deletions android/src/newarch/IntercomModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,23 @@ public void onFailure(@NonNull IntercomError intercomError) {
}
}

@ReactMethod
public void initialize(String apiKey, String appId, Promise promise) {
try {
Activity activity = getCurrentActivity();
if (activity != null && activity.getApplication() != null) {
IntercomModule.initialize(activity.getApplication(), apiKey, appId);
promise.resolve(true);
} else {
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, "Activity is null");
}
} catch (Exception err) {
Log.e(NAME, "initialize error:");
Log.e(NAME, err.toString());
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, err.toString());
}
}

@ReactMethod
public void setNeedsStatusBarAppearanceUpdate(Promise promise) {
// iOS-only method, no-op on Android
Expand Down
17 changes: 17 additions & 0 deletions android/src/oldarch/IntercomModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,23 @@ public void onFailure(@NonNull IntercomError intercomError) {
}
}

@ReactMethod
public void initialize(String apiKey, String appId, Promise promise) {
try {
Activity activity = getCurrentActivity();
if (activity != null && activity.getApplication() != null) {
IntercomModule.initialize(activity.getApplication(), apiKey, appId);
promise.resolve(true);
} else {
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, "Activity is null");
}
} catch (Exception err) {
Log.e(NAME, "initialize error:");
Log.e(NAME, err.toString());
promise.reject(IntercomErrorCodes.INITIALIZE_ERROR, err.toString());
}
}

public static synchronized void initialize(Application application, String apiKey, String appId) {
String sdkVersion = BuildConfig.INTERCOM_VERSION_NAME;
ReactNativeHeaderInterceptor.setReactNativeVersion(application.getApplicationContext(), sdkVersion);
Expand Down
4 changes: 1 addition & 3 deletions examples/expo-example/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@
[
"@intercom/intercom-react-native",
{
"appId": "YOUR_APP_ID",
"androidApiKey": "android_sdk-YOUR_ANDROID_API_KEY",
"iosApiKey": "ios_sdk-YOUR_IOS_API_KEY"
"useManualInit": true
}
],
"expo-router",
Expand Down
48 changes: 46 additions & 2 deletions examples/expo-example/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
import React, { useEffect, useState } from 'react';
import { SafeAreaView, ScrollView, StatusBar, View, Text } from 'react-native';
import * as Notifications from 'expo-notifications';

import { useIntercom } from '../../hooks/useIntercom';
import { useNotifications } from '../../hooks/useNotifications';
import { INTERCOM_CONFIG } from '../../config/intercom.config';

import Header from '../../components/Header';
import Intercom from '@intercom/intercom-react-native';
import AuthenticationSection from '../../components/AuthenticationSection';
import MessagingSection from '../../components/MessagingSection';
import ContentSection from '../../components/ContentSection';
Expand All @@ -28,6 +30,34 @@ Notifications.setNotificationHandler({
export default function App() {
const intercom = useIntercom();
const notifications = useNotifications();
const [isInitialized, setIsInitialized] = useState(false);
const [initError, setInitError] = useState<string | null>(null);

useEffect(() => {
async function initializeIntercom() {
try {
console.log('Initializing Intercom...');
if (!INTERCOM_CONFIG.apiKey || !INTERCOM_CONFIG.appId) {
console.error('Intercom API key and app ID are required');
return;
}
if (!isInitialized) {
await Intercom.initialize(
INTERCOM_CONFIG.apiKey,
INTERCOM_CONFIG.appId
);
setIsInitialized(true);
setInitError(null);
console.log('Intercom initialized successfully');
}
} catch (error) {
console.error('Failed to initialize Intercom:', error);
setInitError(error instanceof Error ? error.message : 'Unknown error');
}
}

initializeIntercom();
}, [isInitialized]);

return (
<ErrorBoundary>
Expand All @@ -41,6 +71,20 @@ export default function App() {
/>

<ScrollView className="flex-1 px-6 py-4">
<View className="mb-4 p-3 rounded-lg bg-gray-100">
<Text className="text-sm font-semibold text-gray-700">
Intercom Status:{' '}
{initError && (
<Text className="text-red-600">Failed: {initError}</Text>
)}
{isInitialized ? (
<Text className="text-green-600">Initialized</Text>
) : (
<Text className="text-yellow-600">Initializing...</Text>
)}
</Text>
</View>

<AuthenticationSection
loggedUser={intercom.loggedUser}
email={intercom.email}
Expand Down
18 changes: 18 additions & 0 deletions examples/expo-example/config/intercom.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Platform } from 'react-native';

/**
* Intercom configuration
*
* Replace these values with your actual Intercom credentials.
* You can find your API keys and App ID in your Intercom workspace settings:
* https://app.intercom.com/a/apps/<your-app-id>/settings/channels/messenger/install?tab=ios
*
* Note: iOS and Android require different API keys.
*/
export const INTERCOM_CONFIG = {
appId: '<your-app-id>', // Replace with your Intercom App ID
apiKey: Platform.select({
ios: 'ios_sdk-<your-ios-api-key>', // Replace with your iOS API key
android: 'android_sdk-<your-android-api-key>', // Replace with your Android API key
}) as string,
};
2 changes: 1 addition & 1 deletion examples/expo-example/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions ios/IntercomModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ @implementation IntercomModule
NSString *UNREAD_CONVERSATION_COUNT = @"107";
NSString *SET_USER_JWT = @"109";
NSString *SET_AUTH_TOKENS = @"110";
NSString *INITIALIZE_ERROR = @"111";
NSString *SEND_TOKEN_TO_INTERCOM = @"302";
NSString *FETCH_HELP_CENTER_COLLECTIONS = @"901";
NSString *FETCH_HELP_CENTER_COLLECTION = @"902";
Expand All @@ -28,6 +29,19 @@ - (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}

RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
withAppId:(NSString *)appId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
@try {
[IntercomModule initialize:apiKey withAppId:appId];
resolve(@(YES));
} @catch (NSException *exception) {
NSLog(@"initialize error: %@", exception.reason);
reject(INITIALIZE_ERROR, @"Failed to initialize Intercom", [self exceptionToError:exception :INITIALIZE_ERROR :@"initialize"]);
}
}

+ (void)initialize:(nonnull NSString *)apiKey withAppId:(nonnull NSString *)appId {
NSString *version = @"0";

Expand Down
1 change: 1 addition & 0 deletions src/NativeIntercomSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface TurboModuleContent {
}

export interface Spec extends TurboModule {
initialize(apiKey: string, appId: string): Promise<boolean>;
loginUnidentifiedUser(): Promise<boolean>;
loginUserWithUserAttributes(userAttributes: UserAttributes): Promise<boolean>;
logout(): Promise<boolean>;
Expand Down
Loading