Experimental Workflow
Follow these steps to get the Experimental Workflow up and running. It's called experimental for a reason, so proceed with caution!
Install the plugin and dependencies
npx expo install react-native-safari-extension react-native-web@~0.19.6 react-dom@18.2.0
Configure the plugin
Configure the plugin in your app.json
.
- Specify a
folderName
for where your extension files will live. This folder should be in the root of your project. - Optionally define any Swift
dependencies
that you need in your extension. - Make sure you have
expo.web.bundler
set to"metro"
.
{
"expo": {
"name": "myApp",
"plugins": [
[
"react-native-safari-extension",
{
"folderName": "MyExtension",
"dependencies": [{ "name": "SomeSwiftPackage", "version": "5.4.3" }]
}
]
],
"web": {
"bundler": "metro"
}
}
}
Plugin Params
{
// Required: The name of the folder where your extension files live
folderName: string;
// Optional: Any Swift dependencies that you need in your extension
dependencies?: { name: string; version?: string }[];
}
Add your extension files
Add your extension files to a folder with the name provided above This folder should be in the root of your project.
Your file structure must match the expected Extension Files. It's recommended to clone this repo and copy the MyExtension
folder from the examples to get started.
Setup Fast Refresh
Fast Refresh allows you to see your changes immediately without having to rebuild your app. To enable Fast Refresh, you'll need to patch @expo/metro-runtime
.
- First, install
@expo/metro-runtime
andpatch-package
:
npm install @expo/metro-runtime
npm install patch-package -D
- Then, add this to your
package.json
:
"scripts": {
"postinstall": "patch-package"
}
- Next, open
node_modules/@expo/metro-runtime/build/HMRClient.js
and make this change:
- const client = new MetroHMRClient(`${serverScheme}://${window.location.host}/hot`);
+ const host = process.env.EXPO_PUBLIC_SAFARI_EXTENSION_HOSTNAME || "localhost"
+ const port = process.env.EXPO_PUBLIC_SAFARI_EXTENSION_PORT || "8081"
+ const client = new MetroHMRClient(`${serverScheme}://${host}:${port}/hot`);
Note: See Using a Physical Device and Using a Custom Port for more info on how to customize this.
- Next, patch the package with:
npx patch-package @expo/metro-runtime
npm install
- If you're using Expo Router, skip this step. If you're not using Expo Router, import
@expo/metro-runtime
in yourApp.tsx
file as early as possible:
import "@expo/metro-runtime";
- Lastly, in the in your extension's
/src/popup.html
file, ensure that the development script tag is pointing to your development server and is uncommented.
<!-- Using Expo Router? Use this: -->
<script
nonce="e60ed1dc-fe33-11ec-b939-0242ac120002"
src="http://localhost:8081/index.ts.bundle?platform=web&dev=true&hot=false&lazy=true&resolver.environment=client&transform.environment=client"
></script>
<!-- Not using Expo Router? Use this: -->
<script
nonce="e60ed1dc-fe33-11ec-b939-0242ac120002"
src="http://localhost:8081/node_modules/expo/AppEntry.bundle?platform=web&dev=true&hot=false&lazy=true"
></script>
Note: If you're using a phsyical device, make sure to update the
src
to point to your computer's IP address instead oflocalhost
. Make sure to also update the port if you're using a port other than8081
.
Prebuild + build your app
If you are using EAS to build your app, run a build using eas-cli.
eas build --platform ios
Or if you're building locally:
npx expo prebuild -p ios
npx expo run:ios
Now you're ready to view your extension! Once the app has successfully run, open the Safari app, navigate to any webpage, and press the AA
button in the address bar. This will open a context menu. Select Manage Extensions
and enable your extension by switching the toggle on. You should now see your extension as an option in the context menu below Manage Extensions
. Click on your extension to open it.
Setup for production
Before publishing your app, there are a few things you'll need to do:
- Create a static web build for your app:
npx expo export --platform web
- Copy the generated
dist
folder and paste it into your extension'ssrc
folder. - In your extension's
popup.html
file, uncomment the production script tag and comment out the development script tag. Update thesrc
to point to your bundle file generated in step 2. - Re-build your app
<script
nonce="e60ed1dc-fe33-11ec-b939-0242ac120002"
src="./dist/bundles/web-7798f744523ecf58d38a12b9d376308f.js"
defer
></script>
Assets
To load local assets in your extension, you'll need to create an assets/assets/
folder in the root of your extension files. The reason for the terrible folder structure is because in expo web apps, local assets are found at http://localhost:8081/assets/assets/image.png
, so we need to mimic that in our extension.