Skip to content

Commit e1df100

Browse files
feat: Add push notification support for expo (#191)
* feat: Add push notification support for expo * fix: remove wrong readme * chore: improve docs
1 parent f148100 commit e1df100

File tree

6 files changed

+169
-12
lines changed

6 files changed

+169
-12
lines changed

README.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ Add this permission to your `Info.plist`
332332

333333
#### iOS: Push Notifications
334334

335+
>**Note**: You should request user permission to display push notifications.
336+
e.g. [react-native-permissions](https://github.com/zoontek/react-native-permissions)
337+
335338
Add **Push Notifications** and **Background Modes > Remote Notifications** [Details HERE](https://developer.apple.com/documentation/xcode/adding-capabilities-to-your-app)
336339

337340

@@ -470,11 +473,50 @@ The plugin provides props for extra customization. Every time you change the pro
470473
}
471474
```
472475

476+
#### Push notifications
477+
478+
Add the following configurations into your `app.json` or `app.config.js`:
479+
480+
Place your `google-services.json` inside the project's root and link it
481+
482+
```json
483+
{
484+
"expo": {
485+
...
486+
"android": {
487+
"googleServicesFile": "./google-services.json",
488+
...
489+
}
490+
}
491+
```
492+
493+
Add the necessary permission descriptions to infoPlist key.
494+
495+
```json
496+
{
497+
"expo": {
498+
...
499+
"ios": {
500+
...
501+
"infoPlist": {
502+
"NSCameraUsageDescription": "This is just a sample text to access the Camera",
503+
"NSPhotoLibraryUsageDescription": "This is just a sample text to access the Photo Library"
504+
}
505+
...
506+
}
507+
}
508+
}
509+
```
510+
511+
>**Note**: You should request user permission to display push notifications.
512+
e.g. [react-native-permissions](https://github.com/zoontek/react-native-permissions)
513+
473514
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
474515

516+
475517
#### Limitations
476518

477-
- **No push notifications support**: Intercom push notifications currently aren't supported by this config plugin extension. This will be added in the future.
519+
- **No deep links support**: Deep Linking currently is not supported by this config plugin extension. This will be added in the future.
478520

479521

480522
## Methods

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"access": "public"
5555
},
5656
"devDependencies": {
57-
"@expo/config-plugins": "^7.8.4",
57+
"@expo/config-plugins": "^7.9.1",
5858
"@react-native-community/eslint-config": "^2.0.0",
5959
"@types/jest": "^26.0.0",
6060
"@types/mocha": "^8.2.2",

sandboxes/IntercomExpo/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5568,6 +5568,11 @@ react-native-mmkv-storage@^0.9.1:
55685568
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.9.1.tgz#0db7e8c1726713dce68704bb8795dc64096c8cbb"
55695569
integrity sha512-FzSx4PKxK2ocT/OuKGlaVziWZyQYHYLUx9595i1oXY263C5mG19PN5RiBgEGL2S5lK4VGUCzO85GAcsrNPtpOg==
55705570

5571+
react-native-permissions@^4.1.5:
5572+
version "4.1.5"
5573+
resolved "https://registry.yarnpkg.com/react-native-permissions/-/react-native-permissions-4.1.5.tgz#db4d1ddbf076570043f4fd4168f54bb6020aec92"
5574+
integrity sha512-r6VMRacASmtRHS+GZ+5HQCp9p9kiE+UU9magHOZCXZLTJitdTuVHWZRrb4v4oqZGU+zAp3mZhTQftuMMv+WLUg==
5575+
55715576
55725577
version "0.73.6"
55735578
resolved "https://registry.npmjs.org/react-native/-/react-native-0.73.6.tgz"

src/expo-plugins/index.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import {
2+
AndroidConfig,
23
ConfigPlugin,
34
createRunOncePlugin,
4-
withAppDelegate,
5-
AndroidConfig,
6-
withMainApplication,
75
withAndroidManifest,
6+
withAppDelegate,
87
withInfoPlist,
8+
withMainApplication,
99
} from '@expo/config-plugins';
10+
1011
import {
1112
addImports,
1213
appendContentsInsideDeclarationBlock,
@@ -16,20 +17,31 @@ import {
1617
insertContentsInsideObjcFunctionBlock,
1718
} from '@expo/config-plugins/build/ios/codeMod';
1819
import type { IntercomPluginProps, IntercomRegion } from './@types';
20+
import { withIntercomPushNotification } from './withPushNotifications';
1921

2022
const mainApplication: ConfigPlugin<IntercomPluginProps> = (_config, props) =>
2123
withMainApplication(_config, (config) => {
2224
let stringContents = config.modResults.contents;
2325
stringContents = addImports(
2426
stringContents,
25-
['com.intercom.reactnative.IntercomModule;'],
26-
false
27+
['com.intercom.reactnative.IntercomModule'],
28+
config.modResults.language === 'java'
29+
);
30+
31+
// Remove previous code
32+
stringContents = stringContents.replace(
33+
/IntercomModule\.initialize\(.*?\)\s*;?\n?/g,
34+
''
2735
);
36+
2837
stringContents = appendContentsInsideDeclarationBlock(
2938
stringContents,
3039
'onCreate',
31-
`IntercomModule.initialize(this, "${props.androidApiKey}", "${props.appId}");`
40+
`IntercomModule.initialize(this, "${props.androidApiKey}", "${
41+
props.appId
42+
}")${config.modResults.language === 'java' ? ';' : ''}\n`
3243
);
44+
3345
config.modResults.contents = stringContents;
3446
return config;
3547
});
@@ -81,12 +93,20 @@ const appDelegate: ConfigPlugin<IntercomPluginProps> = (_config, props) =>
8193
withAppDelegate(_config, (config) => {
8294
let stringContents = config.modResults.contents;
8395
stringContents = addObjcImports(stringContents, ['<IntercomModule.h>']);
96+
97+
// Remove previous code
98+
stringContents = stringContents.replace(
99+
/\s*\[IntercomModule initialize:@"(.*)" withAppId:@"(.*)"];/g,
100+
''
101+
);
102+
84103
stringContents = insertContentsInsideObjcFunctionBlock(
85104
stringContents,
86105
'application didFinishLaunchingWithOptions:',
87106
`[IntercomModule initialize:@"${props.iosApiKey}" withAppId:@"${props.appId}"];`,
88107
{ position: 'tailBeforeLastReturn' }
89108
);
109+
90110
config.modResults.contents = stringContents;
91111
return config;
92112
});
@@ -105,6 +125,7 @@ const infoPlist: ConfigPlugin<IntercomPluginProps> = (
105125

106126
return newConfig;
107127
};
128+
108129
const withIntercomIOS: ConfigPlugin<IntercomPluginProps> = (config, props) => {
109130
let newConfig = appDelegate(config, props);
110131
newConfig = infoPlist(newConfig, props);
@@ -118,6 +139,7 @@ const withIntercomReactNative: ConfigPlugin<IntercomPluginProps> = (
118139
let newConfig = config;
119140
newConfig = withIntercomAndroid(newConfig, props);
120141
newConfig = withIntercomIOS(newConfig, props);
142+
newConfig = withIntercomPushNotification(newConfig, props);
121143
return newConfig;
122144
};
123145

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {
2+
ConfigPlugin,
3+
withAppDelegate,
4+
withInfoPlist,
5+
} from '@expo/config-plugins';
6+
import type { IntercomPluginProps } from './@types';
7+
import {
8+
addObjcImports,
9+
findObjcFunctionCodeBlock,
10+
insertContentsInsideObjcFunctionBlock,
11+
} from '@expo/config-plugins/build/ios/codeMod';
12+
13+
const appDelegate: ConfigPlugin<IntercomPluginProps> = (_config) =>
14+
withAppDelegate(_config, (config) => {
15+
const pushCode = `
16+
// START INTERCOM PUSH
17+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
18+
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
19+
completionHandler:^(BOOL granted, NSError *_Nullable error) {
20+
}];
21+
[[UIApplication sharedApplication] registerForRemoteNotifications];
22+
// END INTERCOM PUSH
23+
`;
24+
25+
const setDeviceTokenCode = '[IntercomModule setDeviceToken:deviceToken];';
26+
27+
let stringContents = config.modResults.contents;
28+
stringContents = addObjcImports(stringContents, [
29+
'<UserNotifications/UserNotifications.h>',
30+
]);
31+
32+
if (!stringContents.includes(pushCode.trim())) {
33+
stringContents = insertContentsInsideObjcFunctionBlock(
34+
stringContents,
35+
'application didFinishLaunchingWithOptions:',
36+
pushCode,
37+
{ position: 'tailBeforeLastReturn' }
38+
);
39+
}
40+
41+
const didRegisterBlock = findObjcFunctionCodeBlock(
42+
stringContents,
43+
'application didRegisterForRemoteNotificationsWithDeviceToken:'
44+
);
45+
46+
if (!didRegisterBlock?.code.includes(setDeviceTokenCode)) {
47+
stringContents = insertContentsInsideObjcFunctionBlock(
48+
stringContents,
49+
'application didRegisterForRemoteNotificationsWithDeviceToken:',
50+
setDeviceTokenCode,
51+
{ position: 'tailBeforeLastReturn' }
52+
);
53+
}
54+
55+
config.modResults.contents = stringContents;
56+
return config;
57+
});
58+
59+
const infoPlist: ConfigPlugin<IntercomPluginProps> = (_config) => {
60+
const newConfig = withInfoPlist(_config, (config) => {
61+
const keys = { remoteNotification: 'remote-notification' };
62+
63+
if (!config.modResults.UIBackgroundModes) {
64+
config.modResults.UIBackgroundModes = [];
65+
}
66+
67+
if (
68+
config.modResults.UIBackgroundModes?.indexOf(keys.remoteNotification) ===
69+
-1
70+
) {
71+
config.modResults.UIBackgroundModes?.push(keys.remoteNotification);
72+
}
73+
74+
return config;
75+
});
76+
77+
return newConfig;
78+
};
79+
80+
export const withIntercomPushNotification: ConfigPlugin<IntercomPluginProps> = (
81+
config,
82+
props
83+
) => {
84+
let newConfig = config;
85+
newConfig = appDelegate(config, props);
86+
newConfig = infoPlist(config, props);
87+
return newConfig;
88+
};

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,10 +1204,10 @@
12041204
minimatch "^3.0.4"
12051205
strip-json-comments "^3.1.1"
12061206

1207-
"@expo/config-plugins@^7.8.4":
1208-
version "7.8.4"
1209-
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.4.tgz#533b5d536c1dc8b5544d64878b51bda28f2e1a1f"
1210-
integrity sha512-hv03HYxb/5kX8Gxv/BTI8TLc9L06WzqAfHRRXdbar4zkLcP2oTzvsLEF4/L/TIpD3rsnYa0KU42d0gWRxzPCJg==
1207+
"@expo/config-plugins@^7.9.1":
1208+
version "7.9.1"
1209+
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.9.1.tgz#fe4f7e4f9d4e87f2dcf2344ffdc59eb466dd5d2e"
1210+
integrity sha512-ICt6Jed1J0tPYMQrJ8K5Qusgih2I6pZ2PU4VSvxsN3T4n97L13XpYV1vyq1Uc/HMl3UhOwldipmgpEbCfeDqsQ==
12111211
dependencies:
12121212
"@expo/config-types" "^50.0.0-alpha.1"
12131213
"@expo/fingerprint" "^0.6.0"

0 commit comments

Comments
 (0)