Skip to content

Commit 82df330

Browse files
authored
Merge pull request #10 from Iterable/MOB-11130-add-firebase-setup-for-android
[MOB-11130] add-firebase-setup-for-android
2 parents e678a36 + f0f3877 commit 82df330

File tree

8 files changed

+253
-4
lines changed

8 files changed

+253
-4
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = {
1717
],
1818
parser: '@typescript-eslint/parser',
1919
parserOptions: {
20-
project: true,
20+
project: ['./tsconfig.json', './plugin/tsconfig.json'],
2121
tsconfigRootDir: __dirname,
2222
},
2323
rules: {

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,4 @@ yarn-error.log
5959
# Generated files
6060
example/ios/
6161
example/android/
62+
example/google-services.json

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
## Instructions
44

5+
### Adding push capabilities to android
6+
7+
Add the path to your google-services.json file to the app file under
8+
`expo.android.googleServicesFile`. EG: If the google services file was added to
9+
the root of the app, the expo file would look like this:
10+
```json
11+
{
12+
"expo": {
13+
"android": {
14+
"googleServicesFile": "./google-services.json"
15+
}
16+
}
17+
}
18+
```
19+
520
### Adding Deeplinks
621

722
#### iOS

example/app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@
4444
]
4545
]
4646
}
47-
}
47+
}

plugin/src/withPushNotifications/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import { ConfigPlugin, withPlugins } from 'expo/config-plugins';
22

33
import { type ConfigPluginPropsWithDefaults } from '../withIterable.types';
44
import { withIosPushNotifications } from './withIosPushNotifications';
5+
import { withAndroidPushNotifications } from './withAndroidPushNotifications';
56

67
export const withPushNotifications: ConfigPlugin<
78
ConfigPluginPropsWithDefaults
89
> = (config, props) => {
9-
return withPlugins(config, [[withIosPushNotifications, props]]);
10+
return withPlugins(config, [
11+
[withIosPushNotifications, props],
12+
[withAndroidPushNotifications, props],
13+
]);
1014
};
1115

1216
export default withPushNotifications;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const GOOGLE_SERVICES_CLASS_PATH = 'com.google.gms:google-services';
2+
export const GOOGLE_SERVICES_PLUGIN = 'com.google.gms.google-services';
3+
export const GOOGLE_SERVICES_VERSION = '4.4.2';
4+
5+
export const FIREBASE_BOM_CLASS_PATH = 'com.google.firebase:firebase-bom';
6+
export const FIREBASE_BOM_VERSION = '32.8.1';
7+
8+
export const FIREBASE_MESSAGING_CLASS_PATH =
9+
'com.google.firebase:firebase-messaging';
10+
11+
export const DEFAULT_GOOGLE_SERVICES_PATH = 'app/google-services.json';
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import {
2+
ConfigPlugin,
3+
WarningAggregator,
4+
withAppBuildGradle,
5+
withDangerousMod,
6+
withPlugins,
7+
withProjectBuildGradle,
8+
} from 'expo/config-plugins';
9+
10+
import { ConfigPluginPropsWithDefaults } from '../withIterable.types';
11+
import {
12+
DEFAULT_GOOGLE_SERVICES_PATH,
13+
FIREBASE_BOM_CLASS_PATH,
14+
FIREBASE_BOM_VERSION,
15+
FIREBASE_MESSAGING_CLASS_PATH,
16+
GOOGLE_SERVICES_CLASS_PATH,
17+
GOOGLE_SERVICES_PLUGIN,
18+
GOOGLE_SERVICES_VERSION,
19+
} from './withAndroidPushNotifications.constants';
20+
21+
import path from 'path';
22+
import fs from 'fs';
23+
24+
interface GradleDependency {
25+
classpath: string;
26+
version?: string;
27+
}
28+
29+
/**
30+
* Add a dependency to the project build.gradle file.
31+
*/
32+
function addProjectDependency(buildGradle: string, options: GradleDependency) {
33+
if (!buildGradle.includes(options?.classpath)) {
34+
return buildGradle.replace(
35+
/dependencies\s?{/,
36+
`dependencies {
37+
classpath('${options?.classpath}${
38+
options?.version ? `:${options?.version}` : ''
39+
}')`
40+
);
41+
} else {
42+
return buildGradle;
43+
}
44+
}
45+
46+
interface AppGradleDependency extends GradleDependency {
47+
/**
48+
* The string to add to the dependencies block.
49+
*
50+
* If this is not provided, ${classpath}:${version} will be used.
51+
*/
52+
implementation?: string;
53+
}
54+
55+
/**
56+
* Add a dependency to the app build.gradle file.
57+
*/
58+
function addAppDependency(buildGradle: string, options: AppGradleDependency) {
59+
if (!buildGradle.includes(options?.classpath)) {
60+
const implementationString =
61+
options?.implementation ??
62+
`'${options?.classpath}${
63+
options?.version ? `:${options?.version}` : ''
64+
}'`;
65+
return buildGradle.replace(
66+
/dependencies\s?{/,
67+
// NOTE: awkard spacing is intentional -- it ensure correct alignment in
68+
// the output build.gradle file
69+
`dependencies {
70+
implementation ${implementationString}`
71+
);
72+
} else {
73+
return buildGradle;
74+
}
75+
}
76+
77+
/**
78+
* Add the apply plugin line to the app build.gradle file if it doesn't exist.
79+
*/
80+
function addApplyPlugin(appBuildGradle: string, pluginName: string) {
81+
// Check for `apply plugin: 'com.google.gms.google-services'`
82+
const applyPluginPattern = new RegExp(
83+
`apply\\s+plugin:\\s+['"]${pluginName}['"]`
84+
);
85+
// Check for `plugins { id 'com.google.gms.google-services' }`
86+
const pluginIdPattern = new RegExp(`id\\s+['"]${pluginName}['"]`);
87+
88+
// Make sure the project does not have the plugin already
89+
if (
90+
!appBuildGradle.match(applyPluginPattern) &&
91+
!appBuildGradle.match(pluginIdPattern)
92+
) {
93+
return appBuildGradle + `\napply plugin: '${pluginName}'`;
94+
}
95+
96+
return appBuildGradle;
97+
}
98+
99+
const withFirebaseInProjectBuildGradle: ConfigPlugin<ConfigPluginPropsWithDefaults> =
100+
(config) => {
101+
return withProjectBuildGradle(config, async (newConfig) => {
102+
if (newConfig.modResults.language === 'groovy') {
103+
newConfig.modResults.contents = addProjectDependency(
104+
newConfig.modResults.contents,
105+
{
106+
classpath: GOOGLE_SERVICES_CLASS_PATH,
107+
version: GOOGLE_SERVICES_VERSION,
108+
}
109+
);
110+
} else {
111+
WarningAggregator.addWarningAndroid(
112+
'@iterable/expo-plugin',
113+
`Cannot automatically configure project build.gradle if it's not groovy`
114+
);
115+
}
116+
117+
return newConfig;
118+
});
119+
};
120+
121+
const withFirebaseInAppBuildGradle: ConfigPlugin<ConfigPluginPropsWithDefaults> =
122+
(config) => {
123+
return withAppBuildGradle(config, (newConfig) => {
124+
if (newConfig.modResults.language === 'groovy') {
125+
newConfig.modResults.contents = addApplyPlugin(
126+
newConfig.modResults.contents,
127+
GOOGLE_SERVICES_PLUGIN
128+
);
129+
newConfig.modResults.contents = addAppDependency(
130+
newConfig.modResults.contents,
131+
{
132+
classpath: FIREBASE_MESSAGING_CLASS_PATH,
133+
}
134+
);
135+
newConfig.modResults.contents = addAppDependency(
136+
newConfig.modResults.contents,
137+
{
138+
classpath: FIREBASE_BOM_CLASS_PATH,
139+
version: FIREBASE_BOM_VERSION,
140+
implementation: `platform('${FIREBASE_BOM_CLASS_PATH}:${FIREBASE_BOM_VERSION}')`,
141+
}
142+
);
143+
} else {
144+
WarningAggregator.addWarningAndroid(
145+
'@iterable/expo-plugin',
146+
`Cannot automatically configure app build.gradle if it's not groovy`
147+
);
148+
}
149+
return newConfig;
150+
});
151+
};
152+
153+
/**
154+
* Add the Google Services dependencies to the project and build.gradle file if
155+
* they don't exist.
156+
* @see Step 4.1 https://support.iterable.com/hc/en-us/articles/360045714132-Installing-Iterable-s-React-Native-SDK#step-4-1-set-up-firebase
157+
*/
158+
const withFirebase: ConfigPlugin<ConfigPluginPropsWithDefaults> = (
159+
config,
160+
props
161+
) => {
162+
return withPlugins(config, [
163+
[withFirebaseInProjectBuildGradle, props],
164+
[withFirebaseInAppBuildGradle, props],
165+
]);
166+
};
167+
168+
/**
169+
* Copy `google-services.json`
170+
* TODO: Add this step to the docs
171+
*/
172+
const withCopyAndroidGoogleServices: ConfigPlugin = (config) => {
173+
return withDangerousMod(config, [
174+
'android',
175+
async (config) => {
176+
if (!config.android?.googleServicesFile) {
177+
throw new Error(
178+
'Path to google-services.json is not defined. Please specify the `expo.android.googleServicesFile` field in app.json.'
179+
);
180+
}
181+
182+
const srcPath = path.resolve(
183+
config.modRequest.projectRoot,
184+
config.android.googleServicesFile
185+
);
186+
const destPath = path.resolve(
187+
config.modRequest.platformProjectRoot,
188+
DEFAULT_GOOGLE_SERVICES_PATH
189+
);
190+
191+
try {
192+
await fs.promises.copyFile(srcPath, destPath);
193+
} catch (_) {
194+
throw new Error(
195+
`Cannot copy google-services.json, because the file ${srcPath} doesn't exist. Please provide a valid path in \`app.json\`.`
196+
);
197+
}
198+
return config;
199+
},
200+
]);
201+
};
202+
203+
export const withAndroidPushNotifications: ConfigPlugin<ConfigPluginPropsWithDefaults> =
204+
(config, props) => {
205+
if (!config.android?.googleServicesFile) {
206+
WarningAggregator.addWarningAndroid(
207+
'@iterable/expo-plugin',
208+
`Path to google-services.json is not defined, so push notifications will not be enabled. To enable push notifications, please specify the \`expo.android.googleServicesFile\` field in app.json.`
209+
);
210+
return config;
211+
}
212+
return withPlugins(config, [
213+
[withFirebase, props],
214+
[withCopyAndroidGoogleServices, props],
215+
]);
216+
};
217+
218+
export default withAndroidPushNotifications;

plugin/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["./src/index.ts","./src/withiterable.types.ts","./src/withstoreconfigvalues.ts","./src/withpushnotifications/index.ts","./src/withpushnotifications/withiospushnotifications.constants.ts","./src/withpushnotifications/withiospushnotifications.ts"],"version":"5.8.2"}
1+
{"root":["./src/index.ts","./src/withiterable.types.ts","./src/withstoreconfigvalues.ts","./src/withpushnotifications/index.ts","./src/withpushnotifications/withandroidpushnotifications.constants.ts","./src/withpushnotifications/withandroidpushnotifications.ts","./src/withpushnotifications/withiospushnotifications.constants.ts","./src/withpushnotifications/withiospushnotifications.ts"],"version":"5.8.2"}

0 commit comments

Comments
 (0)