Handling App Clips URL in React Native

Handling App Clips URL in React Native

In this post, We are going to explain the App Clips invocation URL in React Native.

Firstly, I wrote this article when App Clips was introduced. it was just an experimental, then I decided to explore more about App Clips' experiences.

If you checked Apple documentation about App Clips. the App Clips receives an invocation URL that determines what information appears on the App Clips card.

To configure an invocation URL for debugging:

  1. In Xcode, choose Product > Scheme > Edit Scheme and select your App Clip’s scheme.
  2. Select the Run action.
  3. In the Arguments tab, check whether the _XCAppClipURL environment variable is present. When you add an App Clips target to your project, Xcode adds this environment variable for you. If it’s missing, add the environment variable.
  4. Set the environment variable’s value to the invocation URL you want to test.
  5. Enable the variable by checking the checkbox next to it.
  6. Build and run the App Clips to access the test URL you configured from an NSUserActivity object. For more information on accessing the invocation URL, see Responding to Invocations.

The following screenshot shows the sheet to configure an App Clips target’s Run action with a value for the _XCAppClipURL environment variable:

To catch _XCAppClipURL in  the Debug console, We are going to implement some code.  SceneDelegate  have a method called willConnectToSession  This method is called when your app creates or restores an instance of your user interface.

willConnectToSession method will look like this.

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
  if (connectionOptions.userActivities && connectionOptions.userActivities.count > 0) {
     NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject;
     NSLog(@"%@", userActivity.webpageURL.absoluteString);
     self.initialLinkUrl = userActivity.webpageURL.absoluteString;
     
   }
  
}

After that if you run project again you will see _XCAppClipURL in Xcode Debug Console.

Okay. We got the _XCAppClipURL but question is how to pass this url to React Native. Actually I tried Linking  but got nothing. then I decided to write my own module to handle it.

I created AppClipLinkingManager.h and .m files inside AppClip target folder.


#import "AppClipLinkingManager.h"
#import "SceneDelegate.h"

@interface AppClipLinkingManager ()

@end

@implementation AppClipLinkingManager
- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
  return @[];
}

RCT_EXPORT_METHOD(getInitialLink:(RCTPromiseResolveBlock) resolve:(RCTPromiseRejectBlock)reject) {
  UIScene *scene =  UIApplication.sharedApplication.connectedScenes.allObjects.firstObject;
  SceneDelegate *sceneDelegate = (SceneDelegate *)scene.delegate;
  if (sceneDelegate.initialLinkUrl) {
      resolve(sceneDelegate.initialLinkUrl);
    } else {
      reject(@"event_failure", @"no event id returned", nil);
    }
}
@end
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#import <Foundation/Foundation.h>

@interface AppClipLinkingManager : RCTEventEmitter <RCTBridgeModule>

@end

Also  I added a  object inside SceneDelegate

#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>

@property (strong, nonatomic) UIWindow * window;
@property (strong, readwrite) NSString * initialLinkUrl;

@end

import React, {useState, useEffect} from 'react';
import {AppRegistry, Button, View, Text} from 'react-native';
import {NativeModules} from 'react-native';

const AppClip = () => {
  const [url, setURL] = useState('');
  const {AppClipLinkingManager} = NativeModules;
  useEffect(() => {
    AppClipLinkingManager.getInitialLink().then((link) => {
      setURL(link);
    });
  }, []);
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text style={{fontSize: 22}}>React Native App Clip {url}</Text>
      <Button title="Learn More" color="#841584" />
    </View>
  );
};

AppRegistry.registerComponent('AppClip', () => AppClip);

When you run the app, first willConnectToSession will be called. it will set  initialLinkUrl. when you call getInitialLink method from React Native side url will be returned from sceneDelegate

if you successfully do everything correctly. you will see link like below