onesignal-expo-plugin 1.0.0-beta9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintignore ADDED
@@ -0,0 +1,6 @@
1
+ # don't ever lint node_modules
2
+ node_modules
3
+ # don't lint build output (make sure it's set to your correct build folder name)
4
+ build
5
+ # don't lint the examples projects
6
+ examples
package/.eslintrc.js ADDED
@@ -0,0 +1,18 @@
1
+ module.exports = {
2
+ root: true,
3
+ parser: '@typescript-eslint/parser',
4
+ plugins: [
5
+ '@typescript-eslint',
6
+ ],
7
+ parserOptions: {
8
+ project: "tsconfig.json",
9
+ },
10
+ extends: [
11
+ 'eslint:recommended',
12
+ 'plugin:@typescript-eslint/recommended',
13
+ ],
14
+ rules: {
15
+ "@typescript-eslint/no-floating-promises": "error",
16
+ "no-extra-boolean-cast": "off"
17
+ }
18
+ };
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## January 2022
5
+
6
+ ### `1.0.0-beta10` - 01/24/2022
7
+ #### Added
8
+ - ability to configure version numbers used in the NSE target's plist file.
9
+ - `OneSignalLog` class for better console logging.
10
+ - this changelog file.
11
+
12
+ #### Fixed
13
+ - re-running `expo prebuild` will no longer result in duplicate entitlements and dependencies added to native files.
package/EAS.md ADDED
@@ -0,0 +1,21 @@
1
+ # Expo Application Services
2
+ EAS is Expo's cloud-build service.
3
+
4
+ ## [EAS Homepage](https://expo.dev/eas)
5
+
6
+ ## EAS + OneSignal Setup
7
+ To get OneSignal push notifications working in an EAS-built app, see our guide on how to configure your [iOS credentials](IOS_CREDENTIALS_EAS.md).
8
+
9
+ ## How to run an iOS EAS-built app
10
+ ### Run in a production environment
11
+ For production builds, your generated ipa file should to be distributed to the App Store or Testflight.
12
+
13
+
14
+ ### Run in custom development client
15
+ For development/internal distribution builds, you will need to install a [development client](https://docs.expo.dev/development/getting-started/) on your iOS device.
16
+
17
+ 1. Scan the QR code shown in the terminal after EAS build success and install the development client application to your device.
18
+ 2. In the terminal, run `expo start --dev-client` to be able to serve the application to the client.
19
+ 3. Scan the generated QR code to open the dev client and fetch the app bundle from the running Expo process.
20
+ 4. You may be prompted to allow the app to find local area network devices. Approve.
21
+ 5. Rescan the QR code & open the app. At this point, you should see the mero bundler working. As soon as the bundle is complete, your working app should open!
@@ -0,0 +1,138 @@
1
+ # iOS Credentials Guide: OneSignal + EAS
2
+ ## Overview
3
+ To distribute your iOS application, you must set up all the credentials required by Apple. The 3 primary iOS credentials are:
4
+ * Distribution certificate
5
+ * Provisioning profiles
6
+ * Push notification certificate
7
+
8
+ If you are using EAS, you may be letting Expo handle all your credentials. This is fine for your distribution and provisioning credentials. However, the push notification certificate *must* be handled separately since you are using OneSignal.
9
+
10
+ As of 3/22/2022, OneSignal does not support p8 files and EAS does not support p12 files which results in a particlar issue when cloud building through EAS while using the Expo managed credentials service. Since the EAS CLI only prompts for p8 files at the time of writing, the only way to complete the cloud build is to input "n" (no) when prompted for setting up push notifications through the CLI. However, this leads to the identifier not having the push capability required to set up push successfully. Until EAS adds more granular control over credentials or OneSignal adds p8 support or Expo adds p12 support, developers will have no option except to use local credentials only.
11
+
12
+ If you have developed on iOS before, you may be used to XCode helping with syncing things like capabilities across app targets. In Expo/EAS, we are likely not using XCode at all. Thus, we need to make sure that we set up everything manually via the Apple Developers portal.
13
+
14
+ ### TLDR
15
+ To set up push notifications with OneSignal + Expo, you *must* manage all credentials yourself and use local credentials when cloud building through EAS.
16
+
17
+ ---
18
+
19
+ # Setup
20
+
21
+ ## 1. Credentials setup
22
+ To build successfully, you will need to create and use local credentials (one for production and development). Add `"credentialsSource": "local"` to your `eas.json` file.
23
+
24
+ **Example:**
25
+ ```json
26
+ {
27
+ "build": {
28
+ "release": {
29
+ "android": {
30
+ "gradleCommand": ":app:bundleRelease"
31
+ },
32
+ "ios": {
33
+ "credentialsSource": "local",
34
+ "buildConfiguration": "Release"
35
+ }
36
+ },
37
+ "development": {
38
+ "developmentClient": true,
39
+ "distribution": "internal",
40
+ "credentialsSource": "local"
41
+ }
42
+ },
43
+ "cli": {
44
+ "version": ">= 0.40.0"
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## 2. Identifiers
50
+ In your [Apple developer console](https://developer.apple.com) create two identifiers with the following format:
51
+ - `com.example.app` <-- you likely already have this one
52
+ - `com.example.app.OneSignalNotificationServiceExtension`
53
+
54
+ ### When modifying your existing main app identifier:
55
+ - add the App Groups capability like so: `group.<identifier>.onesignal`
56
+ - keep / add the "Push Notifications" capability
57
+
58
+ ### When creating a new identifier for `OneSignalNotificationServiceExtension`:
59
+ - select AppId from the list of identifier types
60
+ - select App when choosing between App and App Clip
61
+ - add the App Groups capability like so: `group.<identifier>.onesignal`
62
+ - do not add the "Push Notifications" capability
63
+
64
+ #### Note on App Groups at Build Time:
65
+ Make sure to build with both identifiers containing the AppGroup simultaneously. To troubleshoot, remove them both and then rebuild with both identifiers' AppGroup capabilities enabled.
66
+
67
+ ## 3. Provisioning
68
+ Create AdHoc (local development) and AppStore (production) provisioning profiles with both identifiers (four provisioning profiles total). Both provisioning profiles should use the same distribution certificate that is used by your app. Download the profiles.
69
+
70
+ ## 4. Add the profiles to your `credentials.json` file:
71
+ Use the same distribution certificate for the `OneSignalNotificationServiceExtension` as is used for your app (no need to create a new cert).
72
+
73
+ We recommend creating a `certs` directory in your project with subdirectories for each build type (e.g: `certs/adhoc` & `certs/appstore`). Doing so, you can more easily switch which provisioning profiles get used at build time.
74
+
75
+ **Directory structure:**
76
+ ```
77
+ /certs
78
+ - /adhoc
79
+ - /appstore
80
+ ```
81
+
82
+ **Example `credentials.json`:**
83
+ ```json
84
+ {
85
+ "ios": {
86
+ "<PROJECT>": {
87
+ "distributionCertificate": {
88
+ "path": "certs/ios_distribution_certificate.p12",
89
+ "password": "***"
90
+ },
91
+ "provisioningProfilePath": "certs/adhoc/***mobileappprofile.mobileprovision"
92
+ },
93
+ "OneSignalNotificationServiceExtension": {
94
+ "distributionCertificate": {
95
+ "path": "certs/ios_distribution_certificate.p12",
96
+ "password": "***"
97
+ },
98
+ "provisioningProfilePath": "certs/adhoc/***onesignalprofile.mobileprovision"
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ **Note:** your push certificate (p12) file should not appear anywhere in the `credentials.json` file.
105
+
106
+ ---
107
+
108
+ # Build
109
+ ### Release
110
+ If you are ready for a production build, you should use the AppStore provisioning profiles you created. Make sure your `credentials.json` file is using the correct profiles.
111
+
112
+ ### Development
113
+ The development build will use the AdHoc provisioning profiles you created. Make sure your `credentials.json` file is using the correct profiles.
114
+
115
+ #### Expo development client
116
+ If you haven't already, add the expo development client to your project:
117
+
118
+ ```sh
119
+ expo install expo-dev-client
120
+ ```
121
+
122
+ Make sure `"developmentClient": true,` is set in your `eas.json` development build profile.
123
+
124
+ ## All Builds
125
+
126
+ ### Run the EAS build command
127
+ ```sh
128
+ eas build --profile <build profile> --platform ios
129
+ ```
130
+ where `<build profile>` refers to the build profile in your `eas.json` file (e.g: "development" vs "release").
131
+
132
+ ### Build success
133
+ At this point, your build should succeed. You can check its progress by opening the build link in the terminal.
134
+
135
+ ---
136
+
137
+ # How to run on your device
138
+ See our [EAS guide](EAS.md) for instructions.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">Welcome to onesignal-expo-plugin 👋</h1>
2
2
  <p>
3
- <img alt="Version" src="https://img.shields.io/badge/version-1.0.0--beta9-blue.svg?cacheSeconds=2592000" />
3
+ <img alt="Version" src="https://img.shields.io/badge/version-1.0.0-blue.svg?cacheSeconds=2592000" />
4
4
  <a href="https://github.com/OneSignal/onesignal-expo-plugin#readme" target="_blank">
5
5
  <img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
6
6
  </a>
@@ -17,7 +17,6 @@
17
17
  * 🏠 [Homepage](https://github.com/OneSignal/onesignal-expo-plugin#readme)
18
18
  * 🖤 [npm](https://www.npmjs.com/package/onesignal-expo-plugin)
19
19
 
20
- ## 🚧 In Beta 🚧
21
20
  ## Overview
22
21
  This plugin is an [Expo Config Plugin](https://docs.expo.dev/guides/config-plugins/). It extends the Expo config to allow customizing the prebuild phase of managed workflow builds (no need to eject to a bare workflow). For the purposes of OneSignal integration, the plugin facilitates automatically generating/configuring the necessary native code files needed to get the [OneSignal React-Native SDK](https://github.com/OneSignal/react-native-onesignal) to work. You can think of adding a plugin as adding custom native code.
23
22
 
@@ -32,6 +31,11 @@ This plugin is an [Expo Config Plugin](https://docs.expo.dev/guides/config-plugi
32
31
 
33
32
  ```sh
34
33
  expo install onesignal-expo-plugin
34
+
35
+ # npm
36
+ npm install react-native-onesignal
37
+
38
+ # yarn
35
39
  yarn add react-native-onesignal
36
40
  ```
37
41
 
@@ -105,15 +109,44 @@ Alternatively, pass the app ID directly to the function:
105
109
  OneSignal.setAppId("YOUR-ONESIGNAL-APP-ID");
106
110
  ```
107
111
 
108
- ## Run
112
+ ### Versioning
113
+ In your configuration file, make sure you set:
114
+
115
+ | Property | Details |
116
+ |-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
117
+ | `version` | Your app version. Corresponds to `CFBundleShortVersionString` on iOS. This value will be used in your NSE* target's plist file. |
118
+ | `ios.buildNumber` | Build number for your iOS standalone app. Corresponds to `CFBundleVersion` and must match Apple's specified format. This value will be used in your NSE* target's plist file. |
119
+
120
+ \* NSE = Notification Service Extension. Learn more about the NSE [here](https://documentation.onesignal.com/docs/service-extensions).
121
+
122
+ ## EAS (Expo Application Services)
123
+ See our [EAS documentation](EAS.md) for help with EAS.
124
+
125
+ ### iOS Credentials: OneSignal + EAS
126
+ To distribute your iOS application via EAS, you will need to ensure your credentials are set up correctly. See our [credentials setup guide for instructions](IOS_CREDENTIALS_EAS.md).
127
+
128
+ ## Prebuild
129
+ Since we are using custom native code (via the OneSignal plugin), we need to generate the native runtime code for the project. By prebuilding, we automatically link and configure the native modules that have implemented CocoaPods, autolinking, and other config plugins. You can think of prebuild like a native code bundler.
130
+
131
+ When you run `expo prebuild` we enter into a custom managed workflow which provides most of the benefits of bare workflows and managed workflows at the same time.
132
+
133
+ ```sh
134
+ expo prebuild
135
+ ```
136
+
109
137
  ```sh
110
- $ expo prebuild --clean
138
+ # nukes changes and rebuilds
139
+ expo prebuild --clean
140
+ ```
111
141
 
112
- # Build your native iOS project
113
- $ expo run:ios
142
+ ## Run
143
+ The following commands will prebuild *and* run your application. Note that for iOS, push notifications will **not** work in the Simulator.
144
+ ```sh
145
+ # Build and run your native iOS project
146
+ expo run:ios
114
147
 
115
- # Build your native Android project
116
- $ expo run:android
148
+ # Build and run your native Android project
149
+ expo run:android
117
150
  ```
118
151
 
119
152
  ---
@@ -6,26 +6,22 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.withOneSignalAndroid = void 0;
8
8
  const config_plugins_1 = require("@expo/config-plugins");
9
- // ---------- ---------- ---------- ----------
10
- const oneSignalGradle = `
11
- buildscript {
12
- repositories {
13
- gradlePluginPortal()
14
- }
15
- dependencies {
16
- classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.10, 0.99.99]'
17
- }
18
- }
19
-
20
- apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'`;
21
- // ---------- ---------- ---------- ----------
9
+ const androidConstants_1 = require("../support/androidConstants");
10
+ const OneSignalLog_1 = require("../support/OneSignalLog");
22
11
  const withGradleBuildConfig = (config) => {
23
12
  return (0, config_plugins_1.withAppBuildGradle)(config, (newConfig) => {
24
- newConfig.modResults.contents = `${oneSignalGradle.trimStart()}\n\n${newConfig.modResults.contents}`;
13
+ let { contents } = newConfig.modResults;
14
+ // make sure we haven't previously added dependencies
15
+ if (!contents.includes(androidConstants_1.ONESIGNAL_GRADLE)) {
16
+ contents = `${androidConstants_1.ONESIGNAL_GRADLE}\n${contents}`;
17
+ }
18
+ else {
19
+ OneSignalLog_1.OneSignalLog.log("OneSignal dependencies already added to build.gradle. Skipping...");
20
+ }
21
+ newConfig.modResults.contents = contents;
25
22
  return newConfig;
26
23
  });
27
24
  };
28
- // ---------- ---------- ---------- ----------
29
25
  const withOneSignalAndroid = (config, props) => {
30
26
  withGradleBuildConfig(config, props);
31
27
  return config;
@@ -3,18 +3,38 @@
3
3
  * Expo config plugin for One Signal (iOS)
4
4
  * @see https://documentation.onesignal.com/docs/react-native-sdk-setup#step-4-install-for-ios-using-cocoapods-for-ios-apps
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
6
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
27
  };
9
28
  Object.defineProperty(exports, "__esModule", { value: true });
10
29
  exports.xcodeProjectAddNse = exports.withOneSignalIos = void 0;
11
30
  const config_plugins_1 = require("@expo/config-plugins");
12
- const fs_1 = __importDefault(require("fs"));
31
+ const fs = __importStar(require("fs"));
13
32
  const xcode_1 = __importDefault(require("xcode"));
14
33
  const iosConstants_1 = require("../support/iosConstants");
15
34
  const updatePodfile_1 = require("../support/updatePodfile");
16
- const updateNSEEntitlements_1 = require("../support/updateNSEEntitlements");
17
- // ---------- ---------- ---------- ----------
35
+ const NseUpdaterManager_1 = __importDefault(require("../support/NseUpdaterManager"));
36
+ const OneSignalLog_1 = require("../support/OneSignalLog");
37
+ const FileManager_1 = require("../support/FileManager");
18
38
  /**
19
39
  * Add 'app-environment' record with current environment to '<project-name>.entitlements' file
20
40
  * @see https://documentation.onesignal.com/docs/react-native-sdk-setup#step-4-install-for-ios-using-cocoapods-for-ios-apps
@@ -55,29 +75,35 @@ const withRemoteNotificationsPermissions = (config) => {
55
75
  */
56
76
  const withAppGroupPermissions = (config) => {
57
77
  const APP_GROUP_KEY = "com.apple.security.application-groups";
58
- return (0, config_plugins_1.withEntitlementsPlist)(config, (newConfig) => {
78
+ return (0, config_plugins_1.withEntitlementsPlist)(config, newConfig => {
59
79
  var _a;
60
80
  if (!Array.isArray(newConfig.modResults[APP_GROUP_KEY])) {
61
81
  newConfig.modResults[APP_GROUP_KEY] = [];
62
82
  }
63
- newConfig.modResults[APP_GROUP_KEY].push(`group.${((_a = newConfig === null || newConfig === void 0 ? void 0 : newConfig.ios) === null || _a === void 0 ? void 0 : _a.bundleIdentifier) || ""}.onesignal`);
83
+ const modResultsArray = newConfig.modResults[APP_GROUP_KEY];
84
+ const entitlement = `group.${((_a = newConfig === null || newConfig === void 0 ? void 0 : newConfig.ios) === null || _a === void 0 ? void 0 : _a.bundleIdentifier) || ""}.onesignal`;
85
+ if (modResultsArray.indexOf(entitlement) !== -1) {
86
+ return newConfig;
87
+ }
88
+ modResultsArray.push(entitlement);
64
89
  return newConfig;
65
90
  });
66
91
  };
67
92
  const withOneSignalNSE = (config, onesignalProps) => {
68
93
  return (0, config_plugins_1.withXcodeProject)(config, async (props) => {
69
- var _a;
94
+ var _a, _b;
70
95
  const options = {
71
96
  iosPath: props.modRequest.platformProjectRoot,
72
97
  bundleIdentifier: (_a = props.ios) === null || _a === void 0 ? void 0 : _a.bundleIdentifier,
73
98
  devTeam: onesignalProps === null || onesignalProps === void 0 ? void 0 : onesignalProps.devTeam,
99
+ bundleVersion: (_b = props.ios) === null || _b === void 0 ? void 0 : _b.buildNumber,
100
+ bundleShortVersion: props === null || props === void 0 ? void 0 : props.version,
74
101
  iPhoneDeploymentTarget: onesignalProps === null || onesignalProps === void 0 ? void 0 : onesignalProps.iPhoneDeploymentTarget
75
102
  };
76
103
  xcodeProjectAddNse(props.modRequest.projectName || "", options, "node_modules/onesignal-expo-plugin/build/support/serviceExtensionFiles/");
77
104
  return props;
78
105
  });
79
106
  };
80
- // ---------- ---------- ---------- ----------
81
107
  const withOneSignalIos = (config, props) => {
82
108
  withAppEnvironment(config, props);
83
109
  withRemoteNotificationsPermissions(config, props);
@@ -87,40 +113,39 @@ const withOneSignalIos = (config, props) => {
87
113
  };
88
114
  exports.withOneSignalIos = withOneSignalIos;
89
115
  function xcodeProjectAddNse(appName, options, sourceDir) {
90
- const { iosPath, devTeam, bundleIdentifier, iPhoneDeploymentTarget } = options;
91
- (0, updatePodfile_1.updatePodfile)(iosPath);
92
- (0, updateNSEEntitlements_1.updateNSEEntitlements)(`group.${bundleIdentifier}.onesignal`);
116
+ const { iosPath, devTeam, bundleIdentifier, bundleVersion, bundleShortVersion, iPhoneDeploymentTarget } = options;
117
+ // not awaiting in order to not block main thread
118
+ (0, updatePodfile_1.updatePodfile)(iosPath).catch(err => { OneSignalLog_1.OneSignalLog.error(err); });
93
119
  const projPath = `${iosPath}/${appName}.xcodeproj/project.pbxproj`;
94
- const targetName = "OneSignalNotificationServiceExtension";
95
120
  const extFiles = [
96
121
  "NotificationService.h",
97
122
  "NotificationService.m",
98
- `${targetName}.entitlements`,
99
- `${targetName}-Info.plist`
123
+ `${iosConstants_1.NSE_TARGET_NAME}.entitlements`,
124
+ `${iosConstants_1.NSE_TARGET_NAME}-Info.plist`
100
125
  ];
101
126
  const xcodeProject = xcode_1.default.project(projPath);
102
- xcodeProject.parse(function (err) {
127
+ xcodeProject.parse(async function (err) {
103
128
  if (err) {
104
- console.log(`Error parsing iOS project: ${JSON.stringify(err)}`);
129
+ OneSignalLog_1.OneSignalLog.log(`Error parsing iOS project: ${JSON.stringify(err)}`);
105
130
  return;
106
131
  }
107
- // Copy in the extension files
108
- fs_1.default.mkdirSync(`${iosPath}/${targetName}`, { recursive: true });
109
- extFiles.forEach(function (extFile) {
110
- let targetFile = `${iosPath}/${targetName}/${extFile}`;
111
- try {
112
- fs_1.default.createReadStream(`${sourceDir}${extFile}`).pipe(fs_1.default.createWriteStream(targetFile));
113
- }
114
- catch (err) {
115
- console.log(err);
116
- }
117
- });
118
- const projObjects = xcodeProject.hash.project.objects;
132
+ /* COPY OVER EXTENSION FILES */
133
+ fs.mkdirSync(`${iosPath}/${iosConstants_1.NSE_TARGET_NAME}`, { recursive: true });
134
+ for (let i = 0; i < extFiles.length; i++) {
135
+ const extFile = extFiles[i];
136
+ const targetFile = `${iosPath}/${iosConstants_1.NSE_TARGET_NAME}/${extFile}`;
137
+ await FileManager_1.FileManager.copyFile(`${sourceDir}${extFile}`, targetFile);
138
+ }
139
+ /* MODIFY COPIED EXTENSION FILES */
140
+ const nseUpdater = new NseUpdaterManager_1.default(iosPath);
141
+ await nseUpdater.updateNSEEntitlements(`group.${bundleIdentifier}.onesignal`);
142
+ await nseUpdater.updateNSEBundleVersion(bundleVersion !== null && bundleVersion !== void 0 ? bundleVersion : iosConstants_1.DEFAULT_BUNDLE_VERSION);
143
+ await nseUpdater.updateNSEBundleShortVersion(bundleShortVersion !== null && bundleShortVersion !== void 0 ? bundleShortVersion : iosConstants_1.DEFAULT_BUNDLE_SHORT_VERSION);
119
144
  // Create new PBXGroup for the extension
120
- let extGroup = xcodeProject.addPbxGroup(extFiles, targetName, targetName);
145
+ const extGroup = xcodeProject.addPbxGroup(extFiles, iosConstants_1.NSE_TARGET_NAME, iosConstants_1.NSE_TARGET_NAME);
121
146
  // Add the new PBXGroup to the top level group. This makes the
122
147
  // files / folder appear in the file explorer in Xcode.
123
- let groups = xcodeProject.hash.project.objects["PBXGroup"];
148
+ const groups = xcodeProject.hash.project.objects["PBXGroup"];
124
149
  Object.keys(groups).forEach(function (key) {
125
150
  if (groups[key].name === undefined) {
126
151
  xcodeProject.addToPbxGroup(extGroup.uuid, key);
@@ -130,37 +155,38 @@ function xcodeProjectAddNse(appName, options, sourceDir) {
130
155
  // Xcode projects don't contain these if there is only one target
131
156
  // An upstream fix should be made to the code referenced in this link:
132
157
  // - https://github.com/apache/cordova-node-xcode/blob/8b98cabc5978359db88dc9ff2d4c015cba40f150/lib/pbxProject.js#L860
158
+ const projObjects = xcodeProject.hash.project.objects;
133
159
  projObjects['PBXTargetDependency'] = projObjects['PBXTargetDependency'] || {};
134
160
  projObjects['PBXContainerItemProxy'] = projObjects['PBXTargetDependency'] || {};
135
- if (!!xcodeProject.pbxTargetByName(targetName)) {
136
- console.log(targetName, "already exists in project. Skipping...");
161
+ if (!!xcodeProject.pbxTargetByName(iosConstants_1.NSE_TARGET_NAME)) {
162
+ OneSignalLog_1.OneSignalLog.log(`${iosConstants_1.NSE_TARGET_NAME} already exists in project. Skipping...`);
137
163
  return;
138
164
  }
139
165
  // Add the NSE target
140
166
  // This adds PBXTargetDependency and PBXContainerItemProxy for you
141
- const nseTarget = xcodeProject.addTarget(targetName, "app_extension", targetName, `${bundleIdentifier}.${targetName}`);
167
+ const nseTarget = xcodeProject.addTarget(iosConstants_1.NSE_TARGET_NAME, "app_extension", iosConstants_1.NSE_TARGET_NAME, `${bundleIdentifier}.${iosConstants_1.NSE_TARGET_NAME}`);
142
168
  // Add build phases to the new target
143
169
  xcodeProject.addBuildPhase(["NotificationService.m"], "PBXSourcesBuildPhase", "Sources", nseTarget.uuid);
144
170
  xcodeProject.addBuildPhase([], "PBXResourcesBuildPhase", "Resources", nseTarget.uuid);
145
171
  xcodeProject.addBuildPhase([], "PBXFrameworksBuildPhase", "Frameworks", nseTarget.uuid);
146
172
  // Edit the Deployment info of the new Target, only IphoneOS and Targeted Device Family
147
173
  // However, can be more
148
- let configurations = xcodeProject.pbxXCBuildConfigurationSection();
149
- for (let key in configurations) {
174
+ const configurations = xcodeProject.pbxXCBuildConfigurationSection();
175
+ for (const key in configurations) {
150
176
  if (typeof configurations[key].buildSettings !== "undefined" &&
151
- configurations[key].buildSettings.PRODUCT_NAME == `"${targetName}"`) {
152
- let buildSettingsObj = configurations[key].buildSettings;
177
+ configurations[key].buildSettings.PRODUCT_NAME == `"${iosConstants_1.NSE_TARGET_NAME}"`) {
178
+ const buildSettingsObj = configurations[key].buildSettings;
153
179
  buildSettingsObj.DEVELOPMENT_TEAM = devTeam;
154
180
  buildSettingsObj.IPHONEOS_DEPLOYMENT_TARGET = iPhoneDeploymentTarget !== null && iPhoneDeploymentTarget !== void 0 ? iPhoneDeploymentTarget : iosConstants_1.IPHONEOS_DEPLOYMENT_TARGET;
155
181
  buildSettingsObj.TARGETED_DEVICE_FAMILY = iosConstants_1.TARGETED_DEVICE_FAMILY;
156
- buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${targetName}/${targetName}.entitlements`;
182
+ buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${iosConstants_1.NSE_TARGET_NAME}/${iosConstants_1.NSE_TARGET_NAME}.entitlements`;
157
183
  buildSettingsObj.CODE_SIGN_STYLE = "Automatic";
158
184
  }
159
185
  }
160
186
  // Add development teams to both your target and the original project
161
187
  xcodeProject.addTargetAttribute("DevelopmentTeam", devTeam, nseTarget);
162
188
  xcodeProject.addTargetAttribute("DevelopmentTeam", devTeam);
163
- fs_1.default.writeFileSync(projPath, xcodeProject.writeSync());
189
+ fs.writeFileSync(projPath, xcodeProject.writeSync());
164
190
  });
165
191
  }
166
192
  exports.xcodeProjectAddNse = xcodeProjectAddNse;
@@ -19,22 +19,40 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.ReaderManager = void 0;
22
+ exports.FileManager = void 0;
23
23
  const fs = __importStar(require("fs"));
24
- class ReaderManager {
25
- static async readFile(path, replace) {
24
+ const OneSignalLog_1 = require("./OneSignalLog");
25
+ /**
26
+ * FileManager contains static *awaitable* file-system functions
27
+ */
28
+ class FileManager {
29
+ static async readFile(path) {
26
30
  return new Promise((resolve, reject) => {
27
31
  fs.readFile(path, 'utf8', (err, data) => {
28
- if (err) {
32
+ if (err || !data) {
33
+ OneSignalLog_1.OneSignalLog.error("Couldn't read file:" + path);
29
34
  reject(err);
30
- }
31
- if (!data) {
32
- console.error("Couldn't read file:", path);
33
35
  return;
34
36
  }
35
37
  resolve(data);
36
38
  });
37
39
  });
38
40
  }
41
+ static async writeFile(path, contents) {
42
+ return new Promise((resolve, reject) => {
43
+ fs.writeFile(path, contents, 'utf8', (err) => {
44
+ if (err) {
45
+ OneSignalLog_1.OneSignalLog.error("Couldn't write file:" + path);
46
+ reject(err);
47
+ return;
48
+ }
49
+ resolve();
50
+ });
51
+ });
52
+ }
53
+ static async copyFile(path1, path2) {
54
+ const fileContents = await FileManager.readFile(path1);
55
+ await FileManager.writeFile(path2, fileContents);
56
+ }
39
57
  }
40
- exports.ReaderManager = ReaderManager;
58
+ exports.FileManager = FileManager;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const FileManager_1 = require("./FileManager");
4
+ const iosConstants_1 = require("./iosConstants");
5
+ // project `ios/OneSignalNotificationServiceExtension` directory
6
+ const entitlementsFileName = `OneSignalNotificationServiceExtension.entitlements`;
7
+ const plistFileName = `OneSignalNotificationServiceExtension-Info.plist`;
8
+ class NseUpdaterManager {
9
+ constructor(iosPath) {
10
+ this.nsePath = '';
11
+ this.nsePath = `${iosPath}/${iosConstants_1.NSE_TARGET_NAME}`;
12
+ }
13
+ async updateNSEEntitlements(groupIdentifier) {
14
+ const entitlementsFilePath = `${this.nsePath}/${entitlementsFileName}`;
15
+ let entitlementsFile = await FileManager_1.FileManager.readFile(entitlementsFilePath);
16
+ entitlementsFile = entitlementsFile.replace(iosConstants_1.GROUP_IDENTIFIER_TEMPLATE_REGEX, groupIdentifier);
17
+ await FileManager_1.FileManager.writeFile(entitlementsFilePath, entitlementsFile);
18
+ }
19
+ async updateNSEBundleVersion(version) {
20
+ const plistFilePath = `${this.nsePath}/${plistFileName}`;
21
+ let plistFile = await FileManager_1.FileManager.readFile(plistFilePath);
22
+ plistFile = plistFile.replace(iosConstants_1.BUNDLE_VERSION_TEMPLATE_REGEX, version);
23
+ await FileManager_1.FileManager.writeFile(plistFilePath, plistFile);
24
+ }
25
+ async updateNSEBundleShortVersion(version) {
26
+ const plistFilePath = `${this.nsePath}/${plistFileName}`;
27
+ let plistFile = await FileManager_1.FileManager.readFile(plistFilePath);
28
+ plistFile = plistFile.replace(iosConstants_1.BUNDLE_SHORT_VERSION_TEMPLATE_REGEX, version);
29
+ await FileManager_1.FileManager.writeFile(plistFilePath, plistFile);
30
+ }
31
+ }
32
+ exports.default = NseUpdaterManager;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OneSignalLog = void 0;
4
+ class OneSignalLog {
5
+ static log(str) {
6
+ console.log(`\tonesignal-expo-plugin: ${str}`);
7
+ }
8
+ static error(str) {
9
+ console.error(`\tonesignal-expo-plugin: ${str}`);
10
+ }
11
+ }
12
+ exports.OneSignalLog = OneSignalLog;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ONESIGNAL_GRADLE = void 0;
4
+ exports.ONESIGNAL_GRADLE = `buildscript {
5
+ repositories {
6
+ gradlePluginPortal()
7
+ }
8
+ dependencies {
9
+ classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.10, 0.99.99]'
10
+ }
11
+ }
12
+
13
+ apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'`;
@@ -1,11 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GROUP_IDENTIFIER_TEMPLATE_REGEX = exports.NSE_PODFILE_REGEX = exports.NSE_PODFILE_SNIPPET = exports.TARGETED_DEVICE_FAMILY = exports.IPHONEOS_DEPLOYMENT_TARGET = void 0;
3
+ exports.NSE_TARGET_NAME = exports.DEFAULT_BUNDLE_SHORT_VERSION = exports.DEFAULT_BUNDLE_VERSION = exports.BUNDLE_VERSION_TEMPLATE_REGEX = exports.BUNDLE_SHORT_VERSION_TEMPLATE_REGEX = exports.GROUP_IDENTIFIER_TEMPLATE_REGEX = exports.NSE_PODFILE_REGEX = exports.NSE_PODFILE_SNIPPET = exports.TARGETED_DEVICE_FAMILY = exports.IPHONEOS_DEPLOYMENT_TARGET = void 0;
4
4
  exports.IPHONEOS_DEPLOYMENT_TARGET = "11.0";
5
- exports.TARGETED_DEVICE_FAMILY = `\"1,2\"`;
5
+ exports.TARGETED_DEVICE_FAMILY = `"1,2"`;
6
6
  exports.NSE_PODFILE_SNIPPET = `
7
7
  target 'OneSignalNotificationServiceExtension' do
8
8
  pod 'OneSignalXCFramework', '>= 3.0', '< 4.0'
9
9
  end`;
10
10
  exports.NSE_PODFILE_REGEX = /target 'OneSignalNotificationServiceExtension'/;
11
11
  exports.GROUP_IDENTIFIER_TEMPLATE_REGEX = /{{GROUP_IDENTIFIER}}/gm;
12
+ exports.BUNDLE_SHORT_VERSION_TEMPLATE_REGEX = /{{BUNDLE_SHORT_VERSION}}/gm;
13
+ exports.BUNDLE_VERSION_TEMPLATE_REGEX = /{{BUNDLE_VERSION}}/gm;
14
+ exports.DEFAULT_BUNDLE_VERSION = '1';
15
+ exports.DEFAULT_BUNDLE_SHORT_VERSION = '1.0';
16
+ exports.NSE_TARGET_NAME = "OneSignalNotificationServiceExtension";
@@ -17,9 +17,9 @@
17
17
  <key>CFBundlePackageType</key>
18
18
  <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
19
19
  <key>CFBundleShortVersionString</key>
20
- <string>1.0</string>
20
+ <string>{{BUNDLE_SHORT_VERSION}}</string>
21
21
  <key>CFBundleVersion</key>
22
- <string>1</string>
22
+ <string>{{BUNDLE_VERSION}}</string>
23
23
  <key>NSExtension</key>
24
24
  <dict>
25
25
  <key>NSExtensionPointIdentifier</key>
@@ -6,17 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.updatePodfile = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const iosConstants_1 = require("./iosConstants");
9
- const ReaderManager_1 = require("./ReaderManager");
9
+ const OneSignalLog_1 = require("./OneSignalLog");
10
+ const FileManager_1 = require("./FileManager");
10
11
  async function updatePodfile(iosPath) {
11
- const podfile = await ReaderManager_1.ReaderManager.readFile(`${iosPath}/Podfile`);
12
+ const podfile = await FileManager_1.FileManager.readFile(`${iosPath}/Podfile`);
12
13
  const matches = podfile.match(iosConstants_1.NSE_PODFILE_REGEX);
13
14
  if (matches) {
14
- console.info("OneSignalNotificationServiceExtension target already added to Podfile. Skipping...");
15
+ OneSignalLog_1.OneSignalLog.log("OneSignalNotificationServiceExtension target already added to Podfile. Skipping...");
15
16
  }
16
17
  else {
17
18
  fs_1.default.appendFile(`${iosPath}/Podfile`, iosConstants_1.NSE_PODFILE_SNIPPET, (err) => {
18
19
  if (err) {
19
- console.error("Error writing to Podfile");
20
+ OneSignalLog_1.OneSignalLog.error("Error writing to Podfile");
20
21
  }
21
22
  });
22
23
  }
@@ -4,32 +4,26 @@
4
4
  */
5
5
 
6
6
  import { ConfigPlugin, withAppBuildGradle } from '@expo/config-plugins';
7
+ import { ONESIGNAL_GRADLE } from '../support/androidConstants';
8
+ import { OneSignalLog } from '../support/OneSignalLog';
7
9
  import { OneSignalPluginProps } from './withOneSignal';
8
10
 
9
- // ---------- ---------- ---------- ----------
10
- const oneSignalGradle = `
11
- buildscript {
12
- repositories {
13
- gradlePluginPortal()
14
- }
15
- dependencies {
16
- classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.10, 0.99.99]'
17
- }
18
- }
19
-
20
- apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'`;
21
-
22
- // ---------- ---------- ---------- ----------
23
11
  const withGradleBuildConfig: ConfigPlugin<OneSignalPluginProps> = (config) => {
24
12
  return withAppBuildGradle(config, (newConfig) => {
25
- newConfig.modResults.contents = `${oneSignalGradle.trimStart()}\n\n${
26
- newConfig.modResults.contents
27
- }`;
13
+ let { contents } = newConfig.modResults;
14
+
15
+ // make sure we haven't previously added dependencies
16
+ if (!contents.includes(ONESIGNAL_GRADLE)) {
17
+ contents = `${ONESIGNAL_GRADLE}\n${contents}`;
18
+ } else {
19
+ OneSignalLog.log("OneSignal dependencies already added to build.gradle. Skipping...");
20
+ }
21
+
22
+ newConfig.modResults.contents = contents;
28
23
  return newConfig;
29
24
  });
30
25
  };
31
26
 
32
- // ---------- ---------- ---------- ----------
33
27
  export const withOneSignalAndroid: ConfigPlugin<OneSignalPluginProps> = (
34
28
  config,
35
29
  props,
@@ -10,22 +10,30 @@ import {
10
10
  withXcodeProject,
11
11
  } from "@expo/config-plugins";
12
12
  import { OneSignalPluginProps } from "./withOneSignal";
13
- import fs from 'fs';
13
+ import * as fs from 'fs';
14
14
  import xcode from 'xcode';
15
- import { IPHONEOS_DEPLOYMENT_TARGET, TARGETED_DEVICE_FAMILY } from "../support/iosConstants";
15
+ import {
16
+ DEFAULT_BUNDLE_SHORT_VERSION,
17
+ DEFAULT_BUNDLE_VERSION,
18
+ IPHONEOS_DEPLOYMENT_TARGET,
19
+ NSE_TARGET_NAME,
20
+ TARGETED_DEVICE_FAMILY
21
+ } from "../support/iosConstants";
16
22
  import { updatePodfile } from "../support/updatePodfile";
17
- import { updateNSEEntitlements } from "../support/updateNSEEntitlements";
23
+ import NseUpdaterManager from "../support/NseUpdaterManager";
24
+ import { OneSignalLog } from "../support/OneSignalLog";
25
+ import { FileManager } from "../support/FileManager";
18
26
 
19
27
  /* I N T E R F A C E S */
20
28
  interface PluginOptions {
21
- iosPath: string,
22
- devTeam?: string,
23
- bundleIdentifier?: string,
24
- iPhoneDeploymentTarget?: string
29
+ iosPath: string,
30
+ devTeam?: string,
31
+ bundleVersion?: string,
32
+ bundleShortVersion?: string,
33
+ bundleIdentifier?: string,
34
+ iPhoneDeploymentTarget?: string
25
35
  }
26
36
 
27
- // ---------- ---------- ---------- ----------
28
-
29
37
  /**
30
38
  * Add 'app-environment' record with current environment to '<project-name>.entitlements' file
31
39
  * @see https://documentation.onesignal.com/docs/react-native-sdk-setup#step-4-install-for-ios-using-cocoapods-for-ios-apps
@@ -77,13 +85,16 @@ const withAppGroupPermissions: ConfigPlugin<OneSignalPluginProps> = (
77
85
  config
78
86
  ) => {
79
87
  const APP_GROUP_KEY = "com.apple.security.application-groups";
80
- return withEntitlementsPlist(config, (newConfig) => {
88
+ return withEntitlementsPlist(config, newConfig => {
81
89
  if (!Array.isArray(newConfig.modResults[APP_GROUP_KEY])) {
82
90
  newConfig.modResults[APP_GROUP_KEY] = [];
83
91
  }
84
- (newConfig.modResults[APP_GROUP_KEY] as Array<any>).push(
85
- `group.${newConfig?.ios?.bundleIdentifier || ""}.onesignal`
86
- );
92
+ const modResultsArray = (newConfig.modResults[APP_GROUP_KEY] as Array<any>);
93
+ const entitlement = `group.${newConfig?.ios?.bundleIdentifier || ""}.onesignal`;
94
+ if (modResultsArray.indexOf(entitlement) !== -1) {
95
+ return newConfig;
96
+ }
97
+ modResultsArray.push(entitlement);
87
98
 
88
99
  return newConfig;
89
100
  });
@@ -95,6 +106,8 @@ const withOneSignalNSE: ConfigPlugin<OneSignalPluginProps> = (config, onesignalP
95
106
  iosPath: props.modRequest.platformProjectRoot,
96
107
  bundleIdentifier: props.ios?.bundleIdentifier,
97
108
  devTeam: onesignalProps?.devTeam,
109
+ bundleVersion: props.ios?.buildNumber,
110
+ bundleShortVersion: props?.version,
98
111
  iPhoneDeploymentTarget: onesignalProps?.iPhoneDeploymentTarget
99
112
  };
100
113
 
@@ -108,7 +121,6 @@ const withOneSignalNSE: ConfigPlugin<OneSignalPluginProps> = (config, onesignalP
108
121
  });
109
122
  }
110
123
 
111
- // ---------- ---------- ---------- ----------
112
124
  export const withOneSignalIos: ConfigPlugin<OneSignalPluginProps> = (
113
125
  config,
114
126
  props
@@ -120,57 +132,54 @@ export const withOneSignalIos: ConfigPlugin<OneSignalPluginProps> = (
120
132
  return config;
121
133
  };
122
134
 
123
-
124
135
  export function xcodeProjectAddNse(
125
136
  appName: string,
126
137
  options: PluginOptions,
127
138
  sourceDir: string
128
139
  ): void {
129
- const { iosPath, devTeam, bundleIdentifier, iPhoneDeploymentTarget } = options;
140
+ const { iosPath, devTeam, bundleIdentifier, bundleVersion, bundleShortVersion, iPhoneDeploymentTarget } = options;
130
141
 
131
- updatePodfile(iosPath);
132
- updateNSEEntitlements(`group.${bundleIdentifier}.onesignal`)
142
+ // not awaiting in order to not block main thread
143
+ updatePodfile(iosPath).catch(err => { OneSignalLog.error(err) });
133
144
 
134
145
  const projPath = `${iosPath}/${appName}.xcodeproj/project.pbxproj`;
135
- const targetName = "OneSignalNotificationServiceExtension";
136
146
 
137
147
  const extFiles = [
138
148
  "NotificationService.h",
139
149
  "NotificationService.m",
140
- `${targetName}.entitlements`,
141
- `${targetName}-Info.plist`
150
+ `${NSE_TARGET_NAME}.entitlements`,
151
+ `${NSE_TARGET_NAME}-Info.plist`
142
152
  ];
143
153
 
144
154
  const xcodeProject = xcode.project(projPath);
145
155
 
146
- xcodeProject.parse(function(err: Error) {
156
+ xcodeProject.parse(async function(err: Error) {
147
157
  if (err) {
148
- console.log(`Error parsing iOS project: ${JSON.stringify(err)}`);
158
+ OneSignalLog.log(`Error parsing iOS project: ${JSON.stringify(err)}`);
149
159
  return;
150
160
  }
151
161
 
152
- // Copy in the extension files
153
- fs.mkdirSync(`${iosPath}/${targetName}`, { recursive: true });
154
- extFiles.forEach(function (extFile) {
155
- let targetFile = `${iosPath}/${targetName}/${extFile}`;
156
-
157
- try {
158
- fs.createReadStream(`${sourceDir}${extFile}`).pipe(
159
- fs.createWriteStream(targetFile)
160
- );
161
- } catch (err) {
162
- console.log(err);
163
- }
164
- });
162
+ /* COPY OVER EXTENSION FILES */
163
+ fs.mkdirSync(`${iosPath}/${NSE_TARGET_NAME}`, { recursive: true });
165
164
 
166
- const projObjects = xcodeProject.hash.project.objects;
165
+ for(let i = 0; i < extFiles.length; i++) {
166
+ const extFile = extFiles[i];
167
+ const targetFile = `${iosPath}/${NSE_TARGET_NAME}/${extFile}`;
168
+ await FileManager.copyFile(`${sourceDir}${extFile}`, targetFile);
169
+ }
170
+
171
+ /* MODIFY COPIED EXTENSION FILES */
172
+ const nseUpdater = new NseUpdaterManager(iosPath);
173
+ await nseUpdater.updateNSEEntitlements(`group.${bundleIdentifier}.onesignal`)
174
+ await nseUpdater.updateNSEBundleVersion(bundleVersion ?? DEFAULT_BUNDLE_VERSION);
175
+ await nseUpdater.updateNSEBundleShortVersion(bundleShortVersion ?? DEFAULT_BUNDLE_SHORT_VERSION);
167
176
 
168
177
  // Create new PBXGroup for the extension
169
- let extGroup = xcodeProject.addPbxGroup(extFiles, targetName, targetName);
178
+ const extGroup = xcodeProject.addPbxGroup(extFiles, NSE_TARGET_NAME, NSE_TARGET_NAME);
170
179
 
171
180
  // Add the new PBXGroup to the top level group. This makes the
172
181
  // files / folder appear in the file explorer in Xcode.
173
- let groups = xcodeProject.hash.project.objects["PBXGroup"];
182
+ const groups = xcodeProject.hash.project.objects["PBXGroup"];
174
183
  Object.keys(groups).forEach(function (key) {
175
184
  if (groups[key].name === undefined) {
176
185
  xcodeProject.addToPbxGroup(extGroup.uuid, key);
@@ -181,17 +190,18 @@ export function xcodeProjectAddNse(
181
190
  // Xcode projects don't contain these if there is only one target
182
191
  // An upstream fix should be made to the code referenced in this link:
183
192
  // - https://github.com/apache/cordova-node-xcode/blob/8b98cabc5978359db88dc9ff2d4c015cba40f150/lib/pbxProject.js#L860
193
+ const projObjects = xcodeProject.hash.project.objects;
184
194
  projObjects['PBXTargetDependency'] = projObjects['PBXTargetDependency'] || {};
185
195
  projObjects['PBXContainerItemProxy'] = projObjects['PBXTargetDependency'] || {};
186
196
 
187
- if (!!xcodeProject.pbxTargetByName(targetName)) {
188
- console.log(targetName, "already exists in project. Skipping...");
197
+ if (!!xcodeProject.pbxTargetByName(NSE_TARGET_NAME)) {
198
+ OneSignalLog.log(`${NSE_TARGET_NAME} already exists in project. Skipping...`);
189
199
  return;
190
200
  }
191
201
 
192
202
  // Add the NSE target
193
203
  // This adds PBXTargetDependency and PBXContainerItemProxy for you
194
- const nseTarget = xcodeProject.addTarget(targetName, "app_extension", targetName, `${bundleIdentifier}.${targetName}`);
204
+ const nseTarget = xcodeProject.addTarget(NSE_TARGET_NAME, "app_extension", NSE_TARGET_NAME, `${bundleIdentifier}.${NSE_TARGET_NAME}`);
195
205
 
196
206
  // Add build phases to the new target
197
207
  xcodeProject.addBuildPhase(
@@ -211,17 +221,17 @@ export function xcodeProjectAddNse(
211
221
 
212
222
  // Edit the Deployment info of the new Target, only IphoneOS and Targeted Device Family
213
223
  // However, can be more
214
- let configurations = xcodeProject.pbxXCBuildConfigurationSection();
215
- for (let key in configurations) {
224
+ const configurations = xcodeProject.pbxXCBuildConfigurationSection();
225
+ for (const key in configurations) {
216
226
  if (
217
227
  typeof configurations[key].buildSettings !== "undefined" &&
218
- configurations[key].buildSettings.PRODUCT_NAME == `"${targetName}"`
228
+ configurations[key].buildSettings.PRODUCT_NAME == `"${NSE_TARGET_NAME}"`
219
229
  ) {
220
- let buildSettingsObj = configurations[key].buildSettings;
230
+ const buildSettingsObj = configurations[key].buildSettings;
221
231
  buildSettingsObj.DEVELOPMENT_TEAM = devTeam;
222
232
  buildSettingsObj.IPHONEOS_DEPLOYMENT_TARGET = iPhoneDeploymentTarget ?? IPHONEOS_DEPLOYMENT_TARGET;
223
233
  buildSettingsObj.TARGETED_DEVICE_FAMILY = TARGETED_DEVICE_FAMILY;
224
- buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${targetName}/${targetName}.entitlements`;
234
+ buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${NSE_TARGET_NAME}/${NSE_TARGET_NAME}.entitlements`;
225
235
  buildSettingsObj.CODE_SIGN_STYLE = "Automatic";
226
236
  }
227
237
  }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "onesignal-expo-plugin",
3
- "version": "1.0.0-beta9",
3
+ "version": "1.0.0",
4
4
  "description": "The OneSignal Expo plugin allows you to use OneSignal without leaving the managed workflow. Developed in collaboration with SweetGreen.",
5
5
  "main": "./app.plugin.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",
8
- "build": "rm -rf build && tsc && cp -a support/serviceExtensionFiles build/support/",
8
+ "build": "yarn run lint && rm -rf build && tsc && cp -a support/serviceExtensionFiles build/support/",
9
+ "lint": "eslint . --ext .ts",
9
10
  "prepare": "yarn run build"
10
11
  },
11
12
  "repository": {
@@ -31,11 +32,14 @@
31
32
  },
32
33
  "homepage": "https://github.com/OneSignal/onesignal-expo-plugin#readme",
33
34
  "devDependencies": {
35
+ "@typescript-eslint/eslint-plugin": "^5.10.1",
36
+ "@typescript-eslint/parser": "^5.10.1",
37
+ "eslint": "^8.8.0",
34
38
  "expo-module-scripts": "^2.0.0",
35
- "readme-md-generator": "^1.0.0"
39
+ "readme-md-generator": "^1.0.0",
40
+ "typescript": "^4.5.5"
36
41
  },
37
42
  "dependencies": {
38
- "react-native-onesignal": "^4.3.1",
39
43
  "xcode": "^3.0.1"
40
44
  }
41
45
  }
@@ -0,0 +1,38 @@
1
+ import * as fs from 'fs';
2
+ import { OneSignalLog } from './OneSignalLog';
3
+
4
+ /**
5
+ * FileManager contains static *awaitable* file-system functions
6
+ */
7
+ export class FileManager {
8
+ static async readFile(path: string): Promise<string> {
9
+ return new Promise<string>((resolve, reject) => {
10
+ fs.readFile(path, 'utf8', (err, data) => {
11
+ if (err || !data) {
12
+ OneSignalLog.error("Couldn't read file:" + path);
13
+ reject(err);
14
+ return;
15
+ }
16
+ resolve(data);
17
+ });
18
+ });
19
+ }
20
+
21
+ static async writeFile(path: string, contents: string): Promise<void> {
22
+ return new Promise<void>((resolve, reject) => {
23
+ fs.writeFile(path, contents, 'utf8', (err) => {
24
+ if (err) {
25
+ OneSignalLog.error("Couldn't write file:" + path);
26
+ reject(err);
27
+ return;
28
+ }
29
+ resolve();
30
+ });
31
+ });
32
+ }
33
+
34
+ static async copyFile(path1: string, path2: string): Promise<void> {
35
+ const fileContents = await FileManager.readFile(path1);
36
+ await FileManager.writeFile(path2, fileContents);
37
+ }
38
+ }
@@ -0,0 +1,39 @@
1
+ import { FileManager } from './FileManager';
2
+ import {
3
+ BUNDLE_SHORT_VERSION_TEMPLATE_REGEX,
4
+ BUNDLE_VERSION_TEMPLATE_REGEX,
5
+ GROUP_IDENTIFIER_TEMPLATE_REGEX,
6
+ NSE_TARGET_NAME
7
+ } from './iosConstants';
8
+
9
+ // project `ios/OneSignalNotificationServiceExtension` directory
10
+ const entitlementsFileName =`OneSignalNotificationServiceExtension.entitlements`;
11
+ const plistFileName = `OneSignalNotificationServiceExtension-Info.plist`;
12
+
13
+ export default class NseUpdaterManager {
14
+ private nsePath = '';
15
+ constructor(iosPath: string) {
16
+ this.nsePath = `${iosPath}/${NSE_TARGET_NAME}`;
17
+ }
18
+
19
+ async updateNSEEntitlements(groupIdentifier: string): Promise<void> {
20
+ const entitlementsFilePath = `${this.nsePath}/${entitlementsFileName}`;
21
+ let entitlementsFile = await FileManager.readFile(entitlementsFilePath);
22
+ entitlementsFile = entitlementsFile.replace(GROUP_IDENTIFIER_TEMPLATE_REGEX, groupIdentifier);
23
+ await FileManager.writeFile(entitlementsFilePath, entitlementsFile);
24
+ }
25
+
26
+ async updateNSEBundleVersion(version: string): Promise<void> {
27
+ const plistFilePath = `${this.nsePath}/${plistFileName}`;
28
+ let plistFile = await FileManager.readFile(plistFilePath);
29
+ plistFile = plistFile.replace(BUNDLE_VERSION_TEMPLATE_REGEX, version);
30
+ await FileManager.writeFile(plistFilePath, plistFile);
31
+ }
32
+
33
+ async updateNSEBundleShortVersion(version: string): Promise<void> {
34
+ const plistFilePath = `${this.nsePath}/${plistFileName}`;
35
+ let plistFile = await FileManager.readFile(plistFilePath);
36
+ plistFile = plistFile.replace(BUNDLE_SHORT_VERSION_TEMPLATE_REGEX, version);
37
+ await FileManager.writeFile(plistFilePath, plistFile);
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ export class OneSignalLog {
2
+ static log(str: string) {
3
+ console.log(`\tonesignal-expo-plugin: ${str}`)
4
+ }
5
+
6
+ static error(str: string) {
7
+ console.error(`\tonesignal-expo-plugin: ${str}`)
8
+ }
9
+ }
@@ -0,0 +1,11 @@
1
+ export const ONESIGNAL_GRADLE =
2
+ `buildscript {
3
+ repositories {
4
+ gradlePluginPortal()
5
+ }
6
+ dependencies {
7
+ classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.10, 0.99.99]'
8
+ }
9
+ }
10
+
11
+ apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'`;
@@ -1,5 +1,5 @@
1
1
  export const IPHONEOS_DEPLOYMENT_TARGET = "11.0";
2
- export const TARGETED_DEVICE_FAMILY = `\"1,2\"`;
2
+ export const TARGETED_DEVICE_FAMILY = `"1,2"`;
3
3
 
4
4
  export const NSE_PODFILE_SNIPPET = `
5
5
  target 'OneSignalNotificationServiceExtension' do
@@ -9,3 +9,10 @@ end`;
9
9
  export const NSE_PODFILE_REGEX = /target 'OneSignalNotificationServiceExtension'/;
10
10
 
11
11
  export const GROUP_IDENTIFIER_TEMPLATE_REGEX = /{{GROUP_IDENTIFIER}}/gm;
12
+ export const BUNDLE_SHORT_VERSION_TEMPLATE_REGEX = /{{BUNDLE_SHORT_VERSION}}/gm;
13
+ export const BUNDLE_VERSION_TEMPLATE_REGEX = /{{BUNDLE_VERSION}}/gm;
14
+
15
+ export const DEFAULT_BUNDLE_VERSION = '1';
16
+ export const DEFAULT_BUNDLE_SHORT_VERSION = '1.0';
17
+
18
+ export const NSE_TARGET_NAME = "OneSignalNotificationServiceExtension";
@@ -17,9 +17,9 @@
17
17
  <key>CFBundlePackageType</key>
18
18
  <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
19
19
  <key>CFBundleShortVersionString</key>
20
- <string>1.0</string>
20
+ <string>{{BUNDLE_SHORT_VERSION}}</string>
21
21
  <key>CFBundleVersion</key>
22
- <string>1</string>
22
+ <string>{{BUNDLE_VERSION}}</string>
23
23
  <key>NSExtension</key>
24
24
  <dict>
25
25
  <key>NSExtensionPointIdentifier</key>
@@ -1,17 +1,18 @@
1
1
  import fs from 'fs';
2
2
  import { NSE_PODFILE_REGEX, NSE_PODFILE_SNIPPET } from './iosConstants';
3
- import { ReaderManager } from './ReaderManager';
3
+ import { OneSignalLog } from './OneSignalLog';
4
+ import { FileManager } from './FileManager';
4
5
 
5
6
  export async function updatePodfile(iosPath: string) {
6
- const podfile = await ReaderManager.readFile(`${iosPath}/Podfile`);
7
+ const podfile = await FileManager.readFile(`${iosPath}/Podfile`);
7
8
  const matches = podfile.match(NSE_PODFILE_REGEX);
8
9
 
9
10
  if (matches) {
10
- console.info("OneSignalNotificationServiceExtension target already added to Podfile. Skipping...");
11
+ OneSignalLog.log("OneSignalNotificationServiceExtension target already added to Podfile. Skipping...");
11
12
  } else {
12
13
  fs.appendFile(`${iosPath}/Podfile`, NSE_PODFILE_SNIPPET, (err) => {
13
14
  if (err) {
14
- console.error("Error writing to Podfile");
15
+ OneSignalLog.error("Error writing to Podfile");
15
16
  }
16
17
  })
17
18
  }
@@ -1,20 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.updateNSEEntitlements = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const iosConstants_1 = require("./iosConstants");
9
- const ReaderManager_1 = require("./ReaderManager");
10
- const fileToEdit = `${__dirname}/serviceExtensionFiles/OneSignalNotificationServiceExtension.entitlements`;
11
- async function updateNSEEntitlements(groupIdentifier) {
12
- let entitlementsFile = await ReaderManager_1.ReaderManager.readFile(fileToEdit);
13
- entitlementsFile = entitlementsFile.replace(iosConstants_1.GROUP_IDENTIFIER_TEMPLATE_REGEX, groupIdentifier);
14
- fs_1.default.writeFile(fileToEdit, entitlementsFile, 'utf-8', (err) => {
15
- if (err) {
16
- console.error("Error updating OneSignal NSE Entitlement File.");
17
- }
18
- });
19
- }
20
- exports.updateNSEEntitlements = updateNSEEntitlements;
@@ -1,18 +0,0 @@
1
- import * as fs from 'fs';
2
-
3
- export class ReaderManager {
4
- static async readFile(path: string, replace?: string): Promise<string> {
5
- return new Promise<string>((resolve, reject) => {
6
- fs.readFile(path, 'utf8', (err, data) => {
7
- if (err) {
8
- reject(err);
9
- }
10
- if (!data) {
11
- console.error("Couldn't read file:", path);
12
- return;
13
- }
14
- resolve(data);
15
- });
16
- });
17
- }
18
- }
@@ -1,16 +0,0 @@
1
- import fs from 'fs';
2
- import { GROUP_IDENTIFIER_TEMPLATE_REGEX } from './iosConstants';
3
- import { ReaderManager } from './ReaderManager';
4
-
5
- const fileToEdit =`${__dirname}/serviceExtensionFiles/OneSignalNotificationServiceExtension.entitlements`;
6
-
7
- export async function updateNSEEntitlements(groupIdentifier: string) {
8
- let entitlementsFile = await ReaderManager.readFile(fileToEdit);
9
- entitlementsFile = entitlementsFile.replace(GROUP_IDENTIFIER_TEMPLATE_REGEX, groupIdentifier);
10
-
11
- fs.writeFile(fileToEdit, entitlementsFile, 'utf-8', (err) => {
12
- if (err) {
13
- console.error("Error updating OneSignal NSE Entitlement File.");
14
- }
15
- })
16
- }