Skip to content

Deeplinking#

In order to make deeplinks fully work, you will need to do some platform specific setup. You can find more information in iOS and Android native documents.

Additional iOS Setup#

After setup is complete on the native side, to properly receive and forward deeplinks to JS module you need to add the following lines to your AppDelegate.mm file:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
 options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
 return [RCTLinkingManager application:app openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}

For further assistance, see the React Navigation documentation on forwarding links in bare React Native apps.

Use Associated Domains for Storyteller universal links. The native iOS URL classifier also recognizes Storyteller URL schemes; if your app receives a custom scheme, register that scheme in Info.plist as required by iOS. The forwarding methods above are for Objective-C++ AppDelegates; Swift AppDelegates must forward the corresponding URL and user-activity callbacks through RCTLinkingManager.

Handling Navigation to Your App#

When users tap a Storyteller action configured to navigate inside your app, you'll receive the userNavigatedToApp event. This is different from onUserActivityOccurred - while that event provides analytics data about user interactions, userNavigatedToApp gives you the actual URL to navigate to within your app.

userNavigatedToApp Event#

userNavigatedToApp: EventEmitter<{ url: string }>;

This event fires when:

  • A user invokes a content action configured as an in-app action
  • The event carries the configured URL for your app to route, for example myapp://product/123

Event Listener Example#

import { useEffect } from 'react';
import StorytellerSdk, { type UserNavigatedToAppEvent } from '@getstoryteller/react-native-storyteller-sdk';

function App() {
  useEffect(() => {
    const subscription = StorytellerSdk.userNavigatedToApp((event: UserNavigatedToAppEvent) => {
      // Parse and handle your app's URL scheme
      // Example: myapp://product/123
    });

    return () => {
      subscription.remove();
    };
  }, []);

  return null; // Render your app navigation here.
}

URL Scheme Configuration#

Configure content actions as in-app navigation in the Storyteller CMS. A custom URL scheme alone does not determine the action type; external-app and web actions use their native handling instead. Subscribe before the user can invoke these actions, route the supplied URL, and call StorytellerSdk.resumePlayer() when returning to paused content if needed.

Example URL schemes:

  • myapp://product/123
  • myapp://category/shoes?sort=price
  • myapp://profile/user456
isStorytellerDeeplink(url: string): boolean;
isStorytellerDeeplink(url: string, callback: (result: { result: boolean }) => void): void;

Checks whether the URL is a Storyteller deeplink. v11 returns a boolean synchronously; the checked v10.8.2 package returns { result: boolean } through its callback.

openDeeplink(url: string): Promise<void>;
openDeeplink(url: string, callback: (result: boolean) => void): void;

In v11+, opens the provided Storyteller deeplink, including supported Story, Page, Clip/category and Sheet destinations. On iOS, the Promise settles after the native operation succeeds or fails. Android resolves after the open request is invoked; a later load failure can occur after resolution and is not reported by onPlayerOpenFailed for openDeeplink(). Handle Promise rejection, but do not treat resolution as proof that content is visible.

In v11+, the spellings isStorytellerDeepLink() and openDeepLink() are also exported aliases. URL classification does not guarantee that the destination exists.

The historical v10.8.2 openDeeplink declaration above types a boolean callback, but its native response differs: iOS sends { result: true } or { result: false }; Android calls back only on error with { result: string }. Check the response object instead of its truthiness. Android supplies no success callback.

Parameters:

  • url - The deeplink URL to open

Use React Native’s Linking API for warm and cold launch URLs. Subscribe early, but queue or retain incoming URLs until initialization succeeds and your app is ready to present content. The excerpt below assumes this component mounts after that readiness step; initialization failure handling belongs to the host.

Example Implementation#

import { useEffect } from 'react';
import { Linking } from 'react-native';
import StorytellerSdk from '@getstoryteller/react-native-storyteller-sdk';

function App() {
  useEffect(() => {
    // Handle deeplink when app is already running
    const handleDeepLink = async (event: { url: string }) => {
      if (StorytellerSdk.isStorytellerDeeplink(event.url)) {
        try {
          await StorytellerSdk.openDeeplink(event.url);
          console.log('Deeplink open request completed');
        } catch (error) {
          console.error('Failed to open deeplink:', error);
        }
      }
    };

    // Subscribe to URL events
    const subscription = Linking.addEventListener('url', handleDeepLink);

    // Check for initial URL when app launches
    Linking.getInitialURL().then((url) => {
      if (url && StorytellerSdk.isStorytellerDeeplink(url)) {
        StorytellerSdk.openDeeplink(url).catch(console.error);
      }
    });

    return () => {
      subscription.remove();
    };
  }, []);

  return null; // Render your app navigation here.
}
import { useEffect } from 'react';
import { Linking } from 'react-native';
import StorytellerSdk from '@getstoryteller/react-native-storyteller-sdk';

function App() {
  useEffect(() => {
    const handleDeepLink = ({ url }: { url: string }) => {
      StorytellerSdk.isStorytellerDeeplink(url, ({ result }) => {
        if (!result) return;
        StorytellerSdk.openDeeplink(url, (response: unknown) => {
          if (typeof response === 'object' && response !== null && 'result' in response) {
            if (response.result === true) console.log('Opened on iOS');
            else console.warn('Deep-link open failed', response.result);
          }
        });
      });
    };

    const subscription = Linking.addEventListener('url', handleDeepLink);
    Linking.getInitialURL().then((url) => {
      if (url) handleDeepLink({ url });
    });
    return () => subscription.remove();
  }, []);

  return null; // Render your app navigation here.
}