How to Integrate Plaid SDK to React Native Using Expo Config Plugins
Tags: reactnative, mobile, tutorial, react
Published: 2021-12-30 16:38:15

Previously, if you wanted to integrate [Plaid React Native SDK](https://github.com/plaid/react-native-plaid-link-sdk) to Expo, you either had to forcefully eject to bare workflow or use a [webview solution](https://github.com/burstware/expo-plaid-link) which could result in some funny unexpected issues such as [this recaptcha pop up](https://github.com/burstware/expo-plaid-link/issues/15).

Well, in case you have stumbled upon my previous guide to [Expo config plugins](https://www.aronberezkin.com/posts/a-step-by-step-guide-to-writing-your-first-expo-config-plugin), you already know that customizing Expo managed workflow is more than possible and therefore, in this tutorial, we will look at a bit more advanced example how to integrate [Plaid](https://plaid.com/), a reliable service to connect financial accounts to our app.

## SETUP

To get started you can use [this Github repository](https://github.com/AronBe/article-expo-plaid-config-plugin) as a starter or just follow these commands:

Initiate a new Expo project - here with typescript for better DX:

```sh
expo init expo-plaid-sdk-integration  -t expo-template-blank-typescript
```

Add the React Native Plaid SDK library:

```sh
yarn add react-native-plaid-link-sdk
```

Update `App.tsx` to:

```tsx
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { PlaidLink, LinkSuccess, LinkExit } from 'react-native-plaid-link-sdk'

export default function App() {
  return (
    <View style={styles.container}>
      <PlaidLink
        tokenConfig={{ token: '#GENERATED_LINK_TOKEN#', noLoadingState: false }}
        onSuccess={(success: LinkSuccess) => console.log(success)}
        onExit={(exit: LinkExit) => console.log(exit)}
      >
        <Text>Add Account</Text>
      </PlaidLink>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
})
```

And include iOS bundleIdentifier and Android package name of your choice to `app.json`:

```json
"expo": {
    ...otherProps,
    "ios": {
        "supportsTablet": true,
        "bundleIdentifier": "com.expo.plaid"
    },
    "android": {
        "adaptiveIcon": {
            "foregroundImage": "./assets/adaptive-icon.png",
            "backgroundColor": "#FFFFFF"
      },
      "package": "com.expo.plaid"
    }
}
```

If you now try running the application through Expo Go, you will likely see this error...

> _TypeError: null is not an object (evaluating '\_reactNative.NativeModules.RNLinksdk.continueFromRedirectUriString')_

...which should be expected.

## PLAID INTEGRATION

To start integrating Plaid we will need to run the app through [expo-dev-client](https://docs.expo.dev/development/getting-started/) which we can achieve either by using `expo run` [commands](https://blog.expo.dev/introducing-expo-run-commands-835ae8da4813) locally or using EAS build process. By building the dev client we will have a custom Expo Go which will add the Plaid library to its bundle, meaning you can use this client until you decide to add again something new that is not supported by it. For simplicity we will stay with the local `run` commands. Then we just need to follow the Plaid [readme](https://github.com/plaid/react-native-plaid-link-sdk) setup guide for both iOS and Android.

### iOS setup

This is actually super simple. The readme suggests adding to `ios/Podfile` following string `pod 'Plaid', '~> <insert latest version>` to pin the latest version, but, in reality, this is optional and we can easily work with the version currently bundled in the library. As a result, the only step we have to do is to initiate the `expo-dev-client` by running:

```sh
expo run:ios
```

After the build process finishes, the app launches without the error we experienced previously. You can press `Add Account` and you should see Plaid error complaining about the token we provided - but about that later.

> _Unexpected environment string value: (null). Expected one of: production, sandbox, or development._

### Android setup

If you run `expo run:android` and press `Add Account` at this stage, you will get an unhandled promise rejection..

> _TypeError: null is not an object (evaluating '\_reactNative.NativeModules.PlaidAndroid.startLinkActivityForResult')_

..because there are actually bunch of steps to make Android work and as the [readme](https://github.com/plaid/react-native-plaid-link-sdk#android-setup) suggests it is due to TurboModules not supporting autolinking. But in general this is nothing we cannot setup ourselves with Expo config plugins.

#### 1) Setup your app id

This change you have to do on Plaid Dashboard at the `API` [page](https://dashboard.plaid.com/team/api) - `Allowed Android Package`. You should configure there the Android package name from `app.json` - `com.expo.plaid`.

#### 2) Update MainApplication.java

Here we have to fullfil 2 requirements:

- `Add import com.plaid.PlaidPackage; to the imports section`
- `Add packages.add(new PlaidPackage()); to List<ReactPackage> getPackages();`

Which we can do only with an **expo config plugin**:

Create in the root of your project `withAndroidPlaid.ts` file and start transpiling it to javascript with this command:

```sh
yarn tsc withAndroidPlaid.ts --watch --skipLibCheck
```

and import it in `app.json` as a plugin:

```json
{
  "expo": {
    ...otherProps,
    "plugins": ["./withAndroidPlaid"]
  }
}
```

Finally, change `withAndroidPlaid.ts` content to following:

```ts
import type { ConfigPlugin } from '@expo/config-plugins'
import { withMainApplication } from '@expo/config-plugins'

function applyPackage(mainApplication: string) {
  const plaidPackageImport = `import com.plaid.PlaidPackage;\n`
  const plaidAddPackage = `packages.add(new PlaidPackage());`

  // Make sure the project does not have the settings already
  if (!mainApplication.includes(plaidPackageImport)) {
    mainApplication = mainApplication.replace(
      /package com.expo.plaid;/,
      `package com.expo.plaid;\n${plaidPackageImport}`
    )
  }

  if (!mainApplication.includes(plaidAddPackage)) {
    mainApplication = mainApplication.replace(
      /return packages;/,
      `
    ${plaidAddPackage}
    return packages;
    `
    )
  }

  return mainApplication
}

const withAndroidPlaid: ConfigPlugin = (expoConfig) => {
  expoConfig = withMainApplication(expoConfig, (config) => {
    config.modResults.contents = applyPackage(config.modResults.contents)
    return config
  })

  return expoConfig
}

export default withAndroidPlaid
```

If you look closely, our plugin is utilizing `withMainApplication`, a [mod](https://docs.expo.dev/guides/config-plugins/#mod-plugins) provided by Expo allowing us to read and modify the content of `MainApplication.java`. We provide the content to our function `applyPackage` where we execute 2 string replacements to insert `plaidPackageImport` and `plaidAddPackage` constants into the file - the changes Plaid readme wanted from us.

> _Our strategy is to simply find a stable part of the file where we can append our changes. Due to the string replacement nature, **it is quite dangerous thing to rely on, because they could change with consequent React Native and Expo updates** - so be sure these replacements still work when you upgrade._

If you run `expo prebuild -p android` you should now see updated `MainApplication.java`. If you did something incorrectly, you may want to discard changes, check your plugin code, and try `prebuild` again.

#### 3) Update app/build.gradle

We need to update dependencies to following:

```java
dependencies {
    // ...
    implementation project(':react-native-plaid-link-sdk')
}
```

Which we can do utilizing `withAppBuildGradle` modifying the file within `withAndroidPlaid` function.

```ts
// ...
expoConfig = withAppBuildGradle(expoConfig, (config) => {
  config.modResults.contents = applyImplementation(config.modResults.contents)
  return config
})
// ...
```

And `applyImplementation` is our custom function following the previous strategy of appending `plaidImplementation` string to the right place of the file.

```ts
function applyImplementation(appBuildGradle: string) {
  const plaidImplementation = `implementation project(':react-native-plaid-link-sdk')`

  // Make sure the project does not have the dependency already
  if (!appBuildGradle.includes(plaidImplementation)) {
    return appBuildGradle.replace(
      /dependencies\s?{/,
      `dependencies {
    ${plaidImplementation}`
    )
  }

  return appBuildGradle
}
```

#### 4) Update settings.gradle

Finally, we have to append following string to `settings.gradle`:

```java
include ':react-native-plaid-link-sdk'
project(':react-native-plaid-link-sdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-plaid-link-sdk/android')
```

Which can be done using `withSettingsGradle` Expo mod:

```ts
expoConfig = withSettingsGradle(expoConfig, (config) => {
  config.modResults.contents = applySettings(config.modResults.contents)
  return config
})
```

And making the changes with our custom `applySettings` function. Notice that we are just concatenating the strings with `plus` symbol given that we don't really care about the exact placement of `plaidSettings` constant.

```ts
function applySettings(gradleSettings: string) {
  const plaidSettings = `include ':react-native-plaid-link-sdk'
  project(':react-native-plaid-link-sdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-plaid-link-sdk/android')`

  // Make sure the project does not have the settings already
  if (!gradleSettings.includes(`include ':react-native-plaid-link-sdk'`)) {
    return gradleSettings + plaidSettings
  }

  return gradleSettings
}
```

> _If you lost track of the changes or applied them incorrectly, you can always go to the solution branch of [this github repository](https://github.com/AronBe/article-expo-plaid-config-plugin/tree/solution) for my final implementation._

> _**Troubleshooting**: I have discovered 2 potential issues when implementing Plaid on Android, so if you are facing build errors or your app is crashing, you might find an answer in these github issues: [Kotlin version](https://github.com/plaid/react-native-plaid-link-sdk/issues/392#issuecomment-868857758) and [OkHttp version](https://github.com/plaid/react-native-plaid-link-sdk/issues/146#issuecomment-648286809)._

After applying all necessary changes you should just run `expo run:android` to build the app with all modifications.

Once the build process finishes and the app launches, you can press `Add Account` and you should see a different Plaid error complaining about configuration - but it is actually about the fake token we have provided.

> _null - unable to open link, please check that your configuration is valid_

### Getting the link token

At this point, you just need to provide a correct link token in `App.tsx`. Normally you would get it from your backend, but for testing purposes we can actually use a very handy [Postman collection](https://github.com/plaid/plaid-postman) provided by Plaid.

After going through the setup and utilizing your `client_id` and `secret` from Plaid dashboard, you can hit `https://sandbox.plaid.com/link/token/create` API endpoint and use the returned `link_token`.

```json
{
  "expiration": "2021-12-25T19:49:22Z",
  "link_token": "link-sandbox-965dbc89-14fc-4122-b900-27a562de6db0",
  "request_id": "AEBgG6EbWGsQ9aR"
}
```

Now pressing `Add Account` should finally open Plaid interface:

![Plaid intro screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3dtlurx6u21ykw7eyd7.png) 

## SUMMARY

This integration might feel scary at first, but in the end we are just doing the same thing over and over - inserting a string into a native file during the build time (or prebuild time more specifically). Note that the Expo mods we used are marked as dangerous given that they rely on our custom string replacement rules and you should expect them to break in the future. But for now, they are the best way how to combine the Expo managed workflow and Plaid React Native SDK.