![@iterable/expo-plugin](./assets/Iterable-Logo.png '@iterable/expo-plugin')

# @iterable/expo-plugin

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code Coverage](https://qlty.sh/badges/7b2b3c7b-27c0-4fed-af07-57b151b30a59/test_coverage.svg)](https://qlty.sh/gh/Iterable/projects/iterable-expo-plugin)
[![Maintainability](https://qlty.sh/badges/7b2b3c7b-27c0-4fed-af07-57b151b30a59/maintainability.svg)](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`.

<!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=3 orderedList=false} -->
<!-- code_chunk_output -->
- [@iterable/expo-plugin](#iterableexpo-plugin)
  - [🚀 Quick Start](#-quick-start)
  - [🔧 Configuration](#-configuration)
    - [Plugin Options](#plugin-options)
    - [Disabling New Architecture](#disabling-new-architecture)
    - [Adding push capabilities](#adding-push-capabilities)
      - [iOS](#ios)
      - [Android](#android)
    - [Adding Deeplinks](#adding-deeplinks)
      - [iOS](#ios-1)
      - [Android](#android-1)
    - [Configuring ProGuard](#configuring-proguard)
    - [Configuring for EAS Builds](#configuring-for-eas-builds)
      - [iOS EAS Build Configuration](#ios-eas-build-configuration)
      - [Finding Your EAS Project ID](#finding-your-eas-project-id)
      - [Troubleshooting EAS Build Issues](#troubleshooting-eas-build-issues)
  - [✅ Requirements and Limitations](#-requirements-and-limitations)
  - [🎉 Features](#-features)
    - [Push Notifications](#push-notifications)
      - [iOS](#ios-2)
      - [Android](#android-2)
    - [Deep Links](#deep-links)
      - [iOS](#ios-3)
      - [Android](#android-3)
  - [⁉️ Troubleshooting](#️-troubleshooting)
    - [Native Module Not Found](#native-module-not-found)
    - [Failed to delete \[ios|android\] code: ENOTEMPTY: directory not empty](#failed-to-delete-iosandroid-code-enotempty-directory-not-empty)
  - [👏 Contributing](#-contributing)
  - [📝 License](#-license)
  - [💬 Support](#-support)
  - [📚 Further Reading](#-further-reading)
<!-- /code_chunk_output -->

## 🚀 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
   ```

2. Add the plugin to to your `app.json` or `app.config.js`

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

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());
     }, []);
   };
   ```

## 🔧 Configuration

Add the plugin to your `app.json` or `app.config.js`:

```json
{
  "expo": {
    "plugins": [
      [
        "@iterable/expo-plugin",
        {
          "appEnvironment": "development",
          "autoConfigurePushNotifications": true,
          "enableTimeSensitivePush": true,
          "requestPermissionsForPushNotifications": true
        }
      ]
    ]
  }
}
```

### 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. <br><br> **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,
so this needs to be disabled in your `app.json`:

```json
{
  "expo": {
    "newArchEnabled": false
  }
}
```

### Adding push capabilities

#### 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_
  directory
- In `app.json`, add the path to the `google-services.json` file to
  `expo.android.googleServicesFile`. EG:

  ```json
  {
    "expo": {
      "android": {
        "googleServicesFile": "./google-services.json"
      }
    }
  }
  ```

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

#### 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:

```json
{
  "expo": {
    "ios": {
      "associatedDomains": [
        "applinks:expo.dev",
        "applinks:iterable.com",
        "applinks:links.anotherone.com"
      ]
    }
  }
}
```

This is the equivalent of adding them through **Signing & Capabilities** in
Xcode, as described in step 5 of [Iterables iOS Univeral Links
Documentation](https://support.iterable.com/hc/en-us/articles/360035496511-iOS-Universal-Links)

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

EG:

```json
{
  "expo": {
    "android": {
      "intentFilters": [
        {
          "action": "MAIN",
          "category": ["LAUNCHER"],
          "autoVerify": true
        },
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "links.example.com",
              // Deep links coming from Iterable are prefixed by "/a/", so include this as the "pathPrefix".
              "pathPrefix": "/a/"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        }
      ]
    }
  }
}
```

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
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:

```json
{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "extraProguardRules": "-keep class org.json.** { *; }"
          }
        }
      ]
    ]
  }
}
```

Learn more in the [Configure Proguard](https://support.iterable.com/hc/en-us/articles/360035019712-Iterable-s-Android-SDK#step-4-configure-proguard) section of Iterables Android SDK setup docs.

### Configuring for EAS Builds

When building your app with EAS Build, you may encounter signing errors related to the `IterableExpoRichPush` notification service extension target that this plugin creates. This target needs to be properly configured in your `app.json` file for EAS builds to succeed.

#### iOS EAS Build Configuration

To resolve signing issues with the `IterableExpoRichPush` target, you need to add the app extension configuration to your `app.json` file. Add the following to your `expo.extra.eas` configuration:

```json
{
  "expo": {
    "ios": { 
      "bundleIdentifier": "your.app.bundle.id" 
    },
    "extra": {
      "eas": {
        "projectId": "YOUR_EAS_PROJECT_ID",
        "build": {
          "experimental": {
            "ios": {
              "appExtensions": [
                {
                  "targetName": "IterableExpoRichPush",
                  "bundleIdentifier": "your.app.bundle.id.IterableExpoRichPush"
                }
              ]
            }
          }
        }
      }
    }
  }
}
```

**Example Configuration:**
If your EAS project ID is `abc123` and your bundle identifier is `com.myapp`:

```json
{
  "expo": {
    "ios": { 
      "bundleIdentifier": "com.myapp" 
    },
    "extra": {
      "eas": {
        "projectId": "abc123",
        "build": {
          "experimental": {
            "ios": {
              "appExtensions": [
                {
                  "targetName": "IterableExpoRichPush",
                  "bundleIdentifier": "com.myapp.IterableExpoRichPush"
                }
              ]
            }
          }
        }
      }
    }
  }
}
```

#### Finding Your EAS Project ID

To find your EAS project ID, run:
```bash
eas project:info
```

#### Troubleshooting EAS Build Issues

If you encounter the error "Signing for 'IterableExpoRichPush' requires a development team" after adding the above configuration:

1. Ensure your EAS credentials are properly configured:
   ```bash
   eas credentials
   ```

2. Verify that your Apple Developer account has the necessary capabilities for push notifications

3. Try clearing the build cache:
   ```bash
   eas build --platform ios --profile development --clear-cache
   ```

4. If issues persist, ensure your bundle identifier follows the correct pattern: `your.main.bundle.id.IterableExpoRichPush`

## ✅ Requirements and Limitations

- From v2.0.2,`@iterable/react-native-sdk` supports React Native's New
  Architecture](https://reactnative.dev/architecture/landing-page) through the
  Interop Layer.  We are in the process of updating the SDK to fully support the
  New Architecture, and suggest using the legacy architecture in the meantime.
  *TLDR;* Use the  New Architecture at your own risk -- you may encounter
  significant issues.
  - See [Disabling New Architecture](#disabling-new-architecture) for
    instructions on how to disable new architecture in your app. 
- 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
  `@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
  previous versions, they are not supported.

## 🎉 Features

### Push Notifications

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
- Manages notification permissions

### Deep Links

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

## ⁉️ Troubleshooting

### Native Module Not Found

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
yarn cache clean
```

2. Reinstall dependencies:

```bash
yarn install
```

3. Rebuild native projects:

```bash
npx expo prebuild --clean
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
command a second time, so just try again.

## 👏 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📝 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
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/)
