React Native App Clip: New Capabilities and Reduced App Size
Over the last few weeks, I've spent a lot of effort on further improving my Expo config plugin react-native-app-clip allows React Native developers to add an iOS App Clip to their app without writing any native code. As a result, I'm releasing a new version today, which includes many improvements.
For a quick summary including some video clips of the new features, please refer to my Twitter thread.
General improvements
Refactored a lot of code and simplified the implementation where possible. Removed a lot of warnings that were still coming up when opening the generated project in Xcode.
Native module
The plugin now comes with a native module that makes it possible to use capabilities from within React Native that otherwise would require native code. The native module exposes the following functions:
isClip() allows determining whether the code is currently run within the App Clip and can be used to apply different content and behaviors for the full app and the App Clip.
displayOverlay() shows the native iOS banner to promote the full app within the App Clip (see Apple Developer Docs).
setSharedCredential() and getSharedCredential() allows sharing login data from the App Clip to the full app so that the user doesn't have to sign in again after downloading the full app (see Apple Developer Docs).
New config options
I added a bunch of config options. The current options are:
- groupIdentifier (string): Configures an app group to share data between App Clip and full app (see [Apple Developer Docs](https://developer.apple.com/documentation/xcode/configuring-app-groups))
- deploymentTarget (string): Sets the deployment target for the App Clip. If you set this to "16.0", your App Clip can be 15 MB instead of 10 MB.
- requestEphemeralUserNotification (boolean): Enables notifications for the App Clip (see [Apple Developer Docs](https://developer.apple.com/documentation/app_clips/enabling_notifications_in_app_clips))
- requestLocationConfirmation (boolean): Allow App Clip access to location data (see [Apple Developer Docs](https://developer.apple.com/documentation/app_clips/confirming_the_user_s_physical_location))
- appleSignin (boolean): Enable "Sign in with Apple" for the App Clip
- excludedPackages (string[]): Packages to exclude from autolinking for the App Clip to reduce bundle size (see below).
Reduce bundle size by excluding dependencies from autolinking
One of the most critical parts of building an App Clip is keeping the app size small, as there is a limit of 15 MB (10 MB if you are targeting iOS < 16). For me, there are three main points to focus on in this matter:
- Hermes: Hermes, the new JavaScript rendering engine of React Native, has one major drawback regarding App Clips: For iOS, the Hermes executable must be shipped with the app. This means that with Hermes enabled, roughly 3.2 MB of the 15 MB limit is already spent on this executable. Therefore, I recommend disabling Hermes when your app includes an App Clip. I'm still experimenting with having Hermes enabled for the full app and building the App Clip without Hermes, but this hasn’t been successful so far.
- JavaScript bundle: Right now, a single JavaScript bundle file is created that is used both within the full app and the App Clip. That means even if you only expose part of your app's functionality in the App Clip; it will still contain the complete JavaScript bundle. I have a working prototype with Expo Router, where two different bundles are generated, and the App Clip bundle only includes the relevant code for the App Clip, but this still needs some polishing.
- Native modules: If you include dependencies that add native modules to your app, this also comes with a cost regarding the app size. Take the popular animation framework Skia for example. Adding @shopify/react-native-skia to your dependencies will add around 6 MB to your app size. This is not a problem for a full-blown app, but it is a lot of space if you only have 15 MB to spend. So I added the **excludedPackages** option to exclude packages from autolinking. So you can add dependencies you need in your full app and exclude them from the App Clip if you don't need them there. I plan to automate this, but for now, you'll have to specify the packages to be excluded manually.
The following screenshot shows my example app, which includes React Native Skia for the full app and therefore brings the app size just over the 15 MB limit (the app doesn't include much else, just some boilerplate code). The App Clip, however, doesn't include Skia and, therefore stays well under the size limit.
<div className="not-prose">
<img src="/images/posts/app-clip-size.png" className="rounded-none" />
</div>
That's all the updates for now; please let me know what you think via Twitter or in the GitHub repo.