Skip to content

Deeplinking

Setup

iOS Setup

Update your app.json to include the associatedDomains for your MovableInk domain. Also update the infoPlist to include the key: movable_ink_universal_link_domains.

Key Description
scheme If you don't already have a custom URI scheme for your app, add one here. This is required to support deeplinking - a scheme of myapp would allow links such as myapp://some/path to open your app.
movable_ink_universal_link_domains The list of MovableInk domains that will open your app. This should match any MovableInk applinks in associatedDomains - Required for using Universal Links | App Links | Deeplinks
{
  "expo": {
    "scheme": "myapp",
    "ios": {
      "associatedDomains": [
        "applinks:mi.domain.com"
      ],
      "infoPlist": {
        "movable_ink_universal_link_domains": [
          "mi.domain.com"
        ],
        "movable_ink_api_key": "API KEY",
        "movable_ink_region": "us" // or "eu"
      }
    }
  }
}

Android Setup

Update your app.json to include the intentFilters for your MovableInk domain.

{
  "expo": {
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "mi.domain.com",
              "pathPrefix": "/p/cpm"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        },
        {
          "action": "VIEW",
          "data": [
            {
              "scheme": "https",
              "host": "mi.domain.com",
              "pathPrefix": "/p/rpm"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        },
        {
          "action": "VIEW",
          "data": [
            {
              "scheme": "https",
              "host": "mi.domain.com",
              "pathPrefix": "/p/gom"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        }
      ]
    }
  }
}

Make sure to update any instances of mi.domain.com to match your MovableInk domain.

Usage

We highly recommend using Expo Routers Native Intents.

Create a special file called +native-intent.tsx at the top level of your project's app directory.

.
|- app/
|  └─ +native-intent.tsx

This file exports a special redirectSystemPath method designed to handle URL/path processing. When invoked, it receives an options object with two attributes: path and initial.

import RNMovableInk from '@movable/react-native-sdk';
import * as Linking from 'expo-linking';

export async function redirectSystemPath({ path, initial }) {
  try {
    console.log('Received Path:', path);

    let actualUrl = path;

    if (initial) {
      const initialUrl = await Linking.getInitialURL();
      console.log('Initial URL from Linking:', initialUrl);

      if (initialUrl) {
        actualUrl = initialUrl;
      }
    }

    // While the parameter is called `path` there is no guarantee that this is a path or a valid URL
    const url = new URL(actualUrl, 'myapp://');
    console.log('Received URL:', url.hostname);

    // Detection of third-party URLs will change based on the provider
    if (url.hostname === 'mi.domain.com') {
      // If this is a MovableInk URL, we need to initialize the SDK here before we can resolve it
      // If you already have `RNMovableInk.start()` in your app initialization code, that's ok as this is idempotent
      // It's important to call this before attempting to resolve any URLs
      RNMovableInk.start();

      const clickthroughLink = await RNMovableInk.resolveURL(url.toString());
      console.log('Resolved Clickthrough Link:', clickthroughLink);

      // Switch over the clickthrough link to determine where to navigate
      switch (clickthroughLink) {
        case 'https://example.com/explore':
          return '/(tabs)/explore';
        case 'https://example.com/special-offer':
          return '/(tabs)/special-offer';
        default:
          return '/(tabs)/index';
      }
    }

    return path;
  } catch {
    // Do not crash inside this function! Instead you should redirect users
    // to a custom route to handle unexpected errors.
    // Here we're just sending them to the home page. You should handle this error more gracefully in a production app.
    return '/(tabs)/index';
  }
}

If you need to store the clickthrough link instead for later use, you can do so by storing it in a global state manager such as Redux, Context API, or any other state management solution you are using.

Expo Linking

If you'd rather use Expo Linking directly, you can read how to set that up here.

Deferred Deeplinking

When a user attempts to open a MovableInk Link that is designated as a deferred deeplink on an iOS device, and they don't already have your app installed, MovableInk will enable Deferred Deeplinking. Instead of being directed to your website experience, they will be shown a page to open the App Store to download your app.

Once downloaded, MovableInk can check the pasteboard for the original link and allow you to open that location inside your app instead.

Before you can use Deferred Deeplinking, you'll need to setup the sdk_install event schema. You can read more about integrating Deferred Deeplinking here.

After you've setup Deferred Deeplinking, you'll need to enable the app install event before calling start:

// If using Deferred Deep Linking, make sure to enable the app install event before calling start
RNMovableInk.setAppInstallEventEnabled(true);
RNMovableInk.start();

// Call this anytime after you've called start when you're ready to check for a deferred deeplink
RNMovableInk.checkPasteboardOnInstall();

Warning

If this is ran on iOS 16+, this will prompt the user for permission to read from the pasteboard.