diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
new file mode 100644
index 0000000..6a31108
--- /dev/null
+++ b/.github/actions/setup/action.yml
@@ -0,0 +1,27 @@
+name: Setup
+description: Setup Node.js and install dependencies
+
+runs:
+ using: composite
+ steps:
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: .nvmrc
+
+ - name: Cache dependencies
+ id: yarn-cache
+ uses: actions/cache@v3
+ with:
+ path: |
+ **/node_modules
+ .yarn/install-state.gz
+ key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
+ ${{ runner.os }}-yarn-
+
+ - name: Install dependencies
+ if: steps.yarn-cache.outputs.cache-hit != 'true'
+ run: yarn install --immutable
+ shell: bash
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 2a72cda..c710f4f 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -10,10 +10,9 @@ Please explain the changes you made
Screenshots if the PR has visual changes.
-| android | ios |
-|--------|--------|
-|
|
|
-|
|
|
+| android | ios |
+| ------------------ | -------------- |
+| ANDROID_SCREENSHOT | IOS_SCREENSHOT |
## 🧐 Testing
diff --git a/.github/workflows/build-and-commit.yml b/.github/workflows/build-and-commit.yml
new file mode 100644
index 0000000..8c8d836
--- /dev/null
+++ b/.github/workflows/build-and-commit.yml
@@ -0,0 +1,56 @@
+# name: Build and Commit
+
+# on:
+# push:
+# branches:
+# - main
+# pull_request:
+# branches:
+# - main
+# workflow_dispatch:
+
+# permissions:
+# contents: write # Required for committing changes
+
+# jobs:
+# build-and-commit:
+# runs-on: ubuntu-latest
+# steps:
+# - name: Checkout
+# uses: actions/checkout@v4
+# with:
+# fetch-depth: 0 # Fetch all history for proper versioning
+# ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+# - name: Setup Node.js
+# uses: actions/setup-node@v4
+# with:
+# node-version: '20'
+# cache: 'yarn'
+
+# - name: Install dependencies
+# run: yarn install
+
+# - name: Build package
+# run: yarn prepare
+
+# - name: Configure Git
+# run: |
+# git config --global user.name 'GitHub Actions'
+# git config --global user.email 'github-actions@github.com'
+
+# - name: Commit build results
+# run: |
+# git add build/ -f
+# if git diff --quiet && git diff --staged --quiet; then
+# echo "No changes to commit"
+# exit 0
+# fi
+
+# if [[ "${{ github.event_name }}" == "pull_request" ]]; then
+# git commit -m "chore: update build files [skip ci]"
+# git push origin HEAD:refs/heads/${{ github.head_ref }}
+# else
+# git commit -m "chore: update build files [skip ci]"
+# git push origin HEAD:refs/heads/${{ github.ref_name }}
+# fi
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..8e718c2
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,74 @@
+name: Build and Lint
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ merge_group:
+ types:
+ - checks_requested
+
+permissions:
+ contents: read # Required for checking out code
+ checks: write # Required for updating check status
+ security-events: write # Required for security scanning
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Dependency Review
+ uses: actions/dependency-review-action@v4
+ with:
+ fail-on-severity: high
+
+ type-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup
+ uses: ./.github/actions/setup
+
+ - name: Type check
+ run: yarn tsc --noEmit
+ build-library:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup
+ uses: ./.github/actions/setup
+
+ - name: Build package
+ run: yarn prepare
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup
+ uses: ./.github/actions/setup
+
+ - name: Lint files
+ run: yarn lint
+
+ misspell:
+ name: runner / misspell
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code.
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - name: misspell
+ uses: reviewdog/action-misspell@9daa94af4357dddb6fd3775de806bc0a8e98d3e4 # v1.26.3
+ with:
+ github_token: ${{ secrets.github_token }}
+ locale: 'US'
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..6d75a05
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,50 @@
+name: Unit Tests
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ merge_group:
+ types:
+ - checks_requested
+
+permissions:
+ contents: read # Required for checking out code
+ checks: write # Required for updating check status
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup
+ uses: ./.github/actions/setup
+
+ - name: Run unit tests with coverage
+ run: yarn test:coverage --maxWorkers=2
+
+ - name: Check coverage thresholds
+ run: |
+ COVERAGE_THRESHOLD=80
+ COVERAGE=$(cat coverage/coverage-summary.json | jq -r '.total.lines.pct')
+ if (( $(echo "$COVERAGE < $COVERAGE_THRESHOLD" | bc -l) )); then
+ echo "Coverage ($COVERAGE%) is below threshold ($COVERAGE_THRESHOLD%)"
+ exit 1
+ fi
+ echo "Coverage ($COVERAGE%) is above threshold ($COVERAGE_THRESHOLD%)"
+
+ - name: Upload coverage to QLTY
+ uses: qltysh/qlty-action/coverage@v1
+ with:
+ token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
+ files: coverage/lcov.info
+ - name: Add test coverage comment
+ id: coverageComment
+ uses: MishaKav/jest-coverage-comment@main
+ with:
+ hide-comment: false
+ coverage-summary-path: ./coverage/coverage-summary.json
diff --git a/README.md b/README.md
index df1d8de..dfcfd6e 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,17 @@
-
-# @iterable/expo-plugin
+
+# @iterable/expo-plugin
+[](https://opensource.org/licenses/MIT)
+[](https://github.com/Iterable/iterable-expo-plugin/actions/workflows/test.yml)
+[](https://qlty.sh/gh/Iterable/projects/iterable-expo-plugin)
+[](https://qlty.sh/gh/Iterable/projects/iterable-expo-plugin)
This config plugin automatically configures your Expo app to work with
[@iterable/react-native-sdk](https://github.com/Iterable/react-native-sdk) when
the native code is generated through `expo prebuild`.
-
@@ -33,48 +37,58 @@ the native code is generated through `expo prebuild`.
-
## 🚀 Quick Start
1. Install the plugin and `@iterable/react-native-sdk` by running the following in your terminal:
- ```bash
- npx expo install @iterable/expo-plugin @iterable/react-native-sdk
- ```
+
+ ```bash
+ npx expo install @iterable/expo-plugin @iterable/react-native-sdk
+ ```
+
2. Add the plugin to to your `app.json` or `app.config.js`
- ```json
- {
- "expo": {
- "plugins": [
- ["@iterable/expo-plugin", {}]
- ]
- }
- }
- ```
+
+ ```json
+ {
+ "expo": {
+ "plugins": [["@iterable/expo-plugin", {}]]
+ }
+ }
+ ```
+
3. After installing and configuring the plugin, rebuild your native projects:
- ```bash
- npx expo prebuild --clean
- ```
- **WARNING**: `prebuild` will delete everything in your ios/android directories.
+
+ ```bash
+ npx expo prebuild --clean
+ ```
+
+ **WARNING**: `prebuild` will delete everything in your ios/android directories.
+
4. Run your ios or android simulator:
- - ios:
- ```bash
- npx expo run:ios
- ```
- - android:
- ```bash
- npx expo run:android
- ```
-5. Import `@iterable/react-native-sdk` and use as needed. EG:
- ```tsx
- import {useEffect} from 'react';
- import {Iterable, IterableConfig} from '@iterable/react-native-sdk';
-
- const App = () => {
- useEffect(() => {
- Iterable.initialize('MY_API_KEY', new IterableConfig());
- }, []);
- }
- ```
+
+ - ios:
+
+ ```bash
+ npx expo run:ios
+ ```
+
+ - android:
+
+ ```bash
+ npx expo run:android
+ ```
+
+5. Import `@iterable/react-native-sdk` and use as needed. EG:
+
+ ```tsx
+ import { useEffect } from 'react';
+ import { Iterable, IterableConfig } from '@iterable/react-native-sdk';
+
+ const App = () => {
+ useEffect(() => {
+ Iterable.initialize('MY_API_KEY', new IterableConfig());
+ }, []);
+ };
+ ```
## 🔧 Configuration
@@ -84,12 +98,15 @@ Add the plugin to your `app.json` or `app.config.js`:
{
"expo": {
"plugins": [
- ["@iterable/expo-plugin", {
- "appEnvironment": "development",
- "autoConfigurePushNotifications": true,
- "enableTimeSensitivePush": true,
- "requestPermissionsForPushNotifications": true,
- }]
+ [
+ "@iterable/expo-plugin",
+ {
+ "appEnvironment": "development",
+ "autoConfigurePushNotifications": true,
+ "enableTimeSensitivePush": true,
+ "requestPermissionsForPushNotifications": true
+ }
+ ]
]
}
}
@@ -97,15 +114,16 @@ Add the plugin to your `app.json` or `app.config.js`:
### Plugin Options
-| Option | Type | Default | Description |
-|--------|------|---------|-------------|
-| `appEnvironment` | `'development'` \| `'production'` | `'development'` | The environment of your app |
-| `autoConfigurePushNotifications` | boolean | `true` | Whether to automatically configure push notifications. Set to `false` if you want to configure push notifications manually.
**WARNING**: Iterable cannot guarantee compatibility with custom push notification configurations. |
-| `enableTimeSensitivePush` | boolean | `true` | Whether to enable time-sensitive push notifications (iOS only) |
-| `requestPermissionsForPushNotifications` | boolean | `false` | Whether to request permissions for push notifications (iOS only) |
+| Option | Type | Default | Description |
+| ---------------------------------------- | --------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `appEnvironment` | `'development'` \| `'production'` | `'development'` | The environment of your app |
+| `autoConfigurePushNotifications` | boolean | `true` | Whether to automatically configure push notifications. Set to `false` if you want to configure push notifications manually.
**WARNING**: Iterable cannot guarantee compatibility with custom push notification configurations. |
+| `enableTimeSensitivePush` | boolean | `true` | Whether to enable time-sensitive push notifications (iOS only) |
+| `requestPermissionsForPushNotifications` | boolean | `false` | Whether to request permissions for push notifications (iOS only) |
### Disabling New Architecture
-`@iterable/react-native-sdk` is *NOT* compatible with Reacts New Architecture,
+
+`@iterable/react-native-sdk` is _NOT_ compatible with Reacts New Architecture,
so this needs to be disabled in your `app.json`:
```json
@@ -118,16 +136,18 @@ so this needs to be disabled in your `app.json`:
### Adding push capabilities
-#### iOS
+#### iOS
+
- [Configure push notifications for iOS in Iterable](https://support.iterable.com/hc/en-us/articles/115000315806-Setting-up-iOS-Push-Notifications)
#### Android
- [Configure push notifications for Android in Iterable](https://support.iterable.com/hc/en-us/articles/115000331943-Setting-up-Android-Push-Notifications)
-- Place your `google-services.json` file in the root of the *example*
+- Place your `google-services.json` file in the root of the _example_
directory
- In `app.json`, add the path to the `google-services.json` file to
- `expo.android.googleServicesFile`. EG:
+ `expo.android.googleServicesFile`. EG:
+
```json
{
"expo": {
@@ -138,28 +158,30 @@ so this needs to be disabled in your `app.json`:
}
```
-### Adding Deeplinks
+### Adding Deeplinks
Deep linking allows users to navigate to specific screens in your app using
URLs.
To set up deep linking in your **Expo** application, [configure deep links in Iterable](https://support.iterable.com/hc/en-us/articles/115002651226-Configuring-Deep-Links-for-Email-or-SMS),
-then follow the below instructions.
+then follow the below instructions.
#### iOS
+
To add deeplinks to your Expo app for use with Iterable on iOS devices, add associated domains
to your `app.json` under the iOS configuration.
-EG:
+EG:
+
```json
{
"expo": {
"ios": {
"associatedDomains": [
- "applinks:expo.dev",
- "applinks:iterable.com",
- "applinks:links.anotherone.com"
- ]
+ "applinks:expo.dev",
+ "applinks:iterable.com",
+ "applinks:links.anotherone.com"
+ ]
}
}
}
@@ -173,11 +195,13 @@ See further documentation about how expo setup of iOS Universal Links
[here](https://docs.expo.dev/linking/ios-universal-links/).
#### Android
+
To add deeplinks to your Expo app for use with Iterable on Android devices, add
URL schemes and intent filters to your `app.json` under the Android
-configuration. These would be in `expo.android.intentFilters`.
+configuration. These would be in `expo.android.intentFilters`.
EG:
+
```json
{
"expo": {
@@ -186,7 +210,7 @@ EG:
{
"action": "MAIN",
"category": ["LAUNCHER"],
- "autoVerify": true,
+ "autoVerify": true
},
{
"action": "VIEW",
@@ -211,21 +235,26 @@ See further documentation about how expo setup of Android App Links
[here](https://docs.expo.dev/linking/android-app-links/).
### Configuring [ProGuard](https://reactnative.dev/docs/signed-apk-android#enabling-proguard-to-reduce-the-size-of-the-apk-optional)
+
If you're using ProGuard when building your Android app, you will need to add
this line of ProGuard configuration to your build: `-keep class org.json.** { *;
}`.
Below is how to do this using Expo:
+
1. Add the
[expo-build-properties](https://www.npmjs.com/package/expo-build-properties)
- plugin by running:
- ```bash
- npx expo install expo-build-properties
- ```
-2. Add the plugin to your *app.json* file
+ plugin by running:
+
+ ```bash
+ npx expo install expo-build-properties
+ ```
+
+2. Add the plugin to your _app.json_ file
3. To the plugin options, add `{"android":{"extraProguardRules":"-keep class org.json.** { *; }"}}`
-The overall code in your *app.json* file should look something like this:
+The overall code in your _app.json_ file should look something like this:
+
```json
{
"expo": {
@@ -248,18 +277,18 @@ Learn more in the [Configure Proguard](https://support.iterable.com/hc/en-us/art
## ✅ Requirements and Limitations
- New Architecture needs to be disabled, as `@iterable/react-native-sdk` does
- not support it. See [Disabling New Architecture](#disabling-new-architecture)
+ not support it. See [Disabling New Architecture](#disabling-new-architecture)
for instructions on how to disable it.
- Your expo app needs to be run as a [development
build](https://docs.expo.dev/develop/development-builds/introduction/) instead
- of through Expo Go. Both
+ of through Expo Go. Both
`@iterable/iterable-expo-plugin` and `@iterable/react-native-sdk` will **NOT** work in Expo Go
as they are reliant on native code, which Expo Go [does not
support](https://expo.dev/blog/expo-go-vs-development-builds#expo-go-limitations).
- `@iterable/iterable-expo-plugin` is intended for managed workflows, and will
- overwrite the files in your `ios` and `android` directories. Any manual
- changes to those directories will be overwritten on the next build.
-- This plugin has been tested on Expo version 52+. While it may work on
+ overwrite the files in your `ios` and `android` directories. Any manual
+ changes to those directories will be overwritten on the next build.
+- This plugin has been tested on Expo version 52+. While it may work on
previous versions, they are not supported.
## 🎉 Features
@@ -269,12 +298,14 @@ Learn more in the [Configure Proguard](https://support.iterable.com/hc/en-us/art
The plugin automatically configures push notifications for both iOS and Android platforms.
#### iOS
+
- Adds bridge to native Iterable code
- Sets up notification service extension
- Configures required entitlements
- Handles notification permissions
#### Android
+
- Adds bridge to native Iterable code
- Configures Firebase integration
- Sets up notification handling
@@ -285,10 +316,12 @@ The plugin automatically configures push notifications for both iOS and Android
The plugin configures deep linking capabilities for both platforms.
#### iOS
+
- Sets up Universal Links
- Configures associated domains
#### Android
+
- Configures App Links
- Sets up intent filters
@@ -299,6 +332,7 @@ The plugin configures deep linking capabilities for both platforms.
If you encounter the error "Your JavaScript code tried to access a native module that doesn't exist in this development client", try:
1. Clean your project:
+
```bash
rm -rf node_modules
rm -rf ios/Pods
@@ -306,11 +340,13 @@ yarn cache clean
```
2. Reinstall dependencies:
+
```bash
yarn install
```
3. Rebuild native projects:
+
```bash
npx expo prebuild --clean
cd ios && pod install && cd ..
@@ -318,8 +354,8 @@ cd ios && pod install && cd ..
### Failed to delete [ios|android] code: ENOTEMPTY: directory not empty
-Sometimes this error appears when running `npx expo prebuild --clean`. It seems
-to be an intermittent bug within expo. It usually works upon running the same
+Sometimes this error appears when running `npx expo prebuild --clean`. It seems
+to be an intermittent bug within expo. It usually works upon running the same
command a second time, so just try again.
## 👏 Contributing
@@ -334,11 +370,13 @@ for details.
## 💬 Support
For support, please:
+
1. Check the [documentation](https://github.com/Iterable/iterable-expo-plugin#readme)
2. Open an [issue](https://github.com/Iterable/iterable-expo-plugin/issues)
3. Contact [Iterable support](https://support.iterable.com/hc/en-us/requests/new)
## 📚 Further Reading
+
- [Installing Iterables React Native
SDK](https://support.iterable.com/hc/en-us/articles/360045714132-Installing-Iterable-s-React-Native-SDK#step-3-7-add-support-for-deep-links)
-- [Expo docs](https://docs.expo.dev/)
\ No newline at end of file
+- [Expo docs](https://docs.expo.dev/)
diff --git a/build/ExpoAdapterIterableModule.d.ts b/build/ExpoAdapterIterableModule.d.ts
new file mode 100644
index 0000000..5c73bf0
--- /dev/null
+++ b/build/ExpoAdapterIterableModule.d.ts
@@ -0,0 +1,6 @@
+import { NativeModule } from 'expo';
+declare class ExpoAdapterIterableModule extends NativeModule {
+}
+declare const _default: ExpoAdapterIterableModule;
+export default _default;
+//# sourceMappingURL=ExpoAdapterIterableModule.d.ts.map
\ No newline at end of file
diff --git a/build/ExpoAdapterIterableModule.d.ts.map b/build/ExpoAdapterIterableModule.d.ts.map
new file mode 100644
index 0000000..467dc95
--- /dev/null
+++ b/build/ExpoAdapterIterableModule.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"ExpoAdapterIterableModule.d.ts","sourceRoot":"","sources":["../src/ExpoAdapterIterableModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,OAAO,yBAA0B,SAAQ,YAAY;CAAG;;AAG/D,wBAEE"}
\ No newline at end of file
diff --git a/build/ExpoAdapterIterableModule.js b/build/ExpoAdapterIterableModule.js
new file mode 100644
index 0000000..38aaa62
--- /dev/null
+++ b/build/ExpoAdapterIterableModule.js
@@ -0,0 +1,4 @@
+import { requireNativeModule } from 'expo';
+// This call loads the native module object from the JSI.
+export default requireNativeModule('ExpoAdapterIterable');
+//# sourceMappingURL=ExpoAdapterIterableModule.js.map
\ No newline at end of file
diff --git a/build/ExpoAdapterIterableModule.js.map b/build/ExpoAdapterIterableModule.js.map
new file mode 100644
index 0000000..f97c2c7
--- /dev/null
+++ b/build/ExpoAdapterIterableModule.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ExpoAdapterIterableModule.js","sourceRoot":"","sources":["../src/ExpoAdapterIterableModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAIzD,yDAAyD;AACzD,eAAe,mBAAmB,CAChC,qBAAqB,CACtB,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\ndeclare class ExpoAdapterIterableModule extends NativeModule {}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule(\n 'ExpoAdapterIterable'\n);\n"]}
\ No newline at end of file
diff --git a/build/index.d.ts b/build/index.d.ts
new file mode 100644
index 0000000..bd9f7b0
--- /dev/null
+++ b/build/index.d.ts
@@ -0,0 +1,3 @@
+import ExpoAdapterIterableModule from './ExpoAdapterIterableModule';
+export default ExpoAdapterIterableModule;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/build/index.d.ts.map b/build/index.d.ts.map
new file mode 100644
index 0000000..ab71e42
--- /dev/null
+++ b/build/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,yBAAyB,MAAM,6BAA6B,CAAC;AAEpE,eAAe,yBAAyB,CAAC"}
\ No newline at end of file
diff --git a/build/index.js b/build/index.js
new file mode 100644
index 0000000..f20e8c8
--- /dev/null
+++ b/build/index.js
@@ -0,0 +1,4 @@
+// Reexport the native module.
+import ExpoAdapterIterableModule from './ExpoAdapterIterableModule';
+export default ExpoAdapterIterableModule;
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/build/index.js.map b/build/index.js.map
new file mode 100644
index 0000000..902e01a
--- /dev/null
+++ b/build/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,yBAAyB,MAAM,6BAA6B,CAAC;AAEpE,eAAe,yBAAyB,CAAC","sourcesContent":["// Reexport the native module.\nimport ExpoAdapterIterableModule from './ExpoAdapterIterableModule';\n\nexport default ExpoAdapterIterableModule;\n"]}
\ No newline at end of file