expo-app-clip
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdd an App Clip to an Expo App
为Expo应用添加App Clip
Requirements. Adding the App Clip target is open source. Shipping one requires an Apple Developer Program membership and App Store review, and the AASA file must be served over HTTPS on your domain (any HTTPS host works; EAS Hosting is one option). Building via EAS Build oruses your EAS plan's build minutes. See https://expo.dev/pricing and https://developer.apple.com/app-clips/.bunx testflight
Adds an iOS App Clip target to an Expo project. The Clip lives in , ships alongside the parent app, and is invoked from a URL on the app's domain via an Apple App Site Association (AASA) file.
targets/clip/The parent app's bundle ID becomes and the Clip's is automatically derived as (e.g. ).
com.<username>.<app-name><parent>.clipcom.bacon.may20.clip要求。添加App Clip目标是开源的。发布App Clip需要Apple开发者计划会员资格和App Store审核,且AASA文件必须通过HTTPS在你的域名上托管(任何HTTPS主机均可;EAS Hosting是其中一个选项)。通过EAS Build或进行构建会消耗你的EAS计划的构建时长。详情请查看https://expo.dev/pricing和https://developer.apple.com/app-clips/。bunx testflight
为Expo项目添加iOS App Clip目标。Clip存放在目录中,与主应用一同分发,可通过应用域名上的URL调用,这依赖于Apple App Site Association(AASA)文件。
targets/clip/主应用的Bundle ID会变为,Clip的Bundle ID会自动派生为(例如)。
com.<username>.<app-name><parent>.clipcom.bacon.may20.clip1. Set bundleIdentifier
and appleTeamId
bundleIdentifierappleTeamId1. 设置bundleIdentifier
和appleTeamId
bundleIdentifierappleTeamIdbun create targetapp.jsonjson
{
"expo": {
"ios": {
"bundleIdentifier": "com.<username>.<app-name>",
"appleTeamId": "XX57RJ5UTD"
}
}
}如果缺少这些配置,会发出警告。请在中添加:
bun create targetapp.jsonjson
{
"expo": {
"ios": {
"bundleIdentifier": "com.<username>.<app-name>",
"appleTeamId": "XX57RJ5UTD"
}
}
}2. Add the App Clip target
2. 添加App Clip目标
sh
bun create target clip- — the target's config plugin
targets/clip/expo-target.config.js - — Clip Info.plist
targets/clip/Info.plist - ,
targets/clip/AppDelegate.swift, etc.Assets.xcassets
Pick a good icon or reuse the existing one defined in the app — check it with under the or key.
bunx expo configiconios.iconsh
bun create target clip- —— 目标的配置插件
targets/clip/expo-target.config.js - —— Clip的Info.plist文件
targets/clip/Info.plist - 、
targets/clip/AppDelegate.swift等文件Assets.xcassets
选择合适的图标,或复用应用中已定义的图标——可通过查看或键下的配置。
bunx expo configiconios.icon3. Wire up associated domains
3. 配置关联域名
The parent app and the Clip each need the Associated Domains entitlement pointing at the domain that hosts the AASA file.
In , add both (parent) and (Clip invocation) entries:
app.jsonapplinks:appclips:json
{
"expo": {
"ios": {
"associatedDomains": [
"applinks:may20.expo.app",
"appclips:may20.expo.app"
]
}
}
}In , declare the Clip's entitlement:
targets/clip/expo-target.config.jsjs
/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = (config) => ({
type: "clip",
icon: "https://github.com/expo.png",
entitlements: {
"com.apple.developer.associated-domains": ["appclips:may20.expo.app"],
},
});If you skip this,will print:expo prebuild.Apple App Clip may require the associated domains entitlement but none were found
主应用和Clip都需要关联域名权限,指向托管AASA文件的域名。
在中,添加(主应用)和(Clip调用)条目:
app.jsonapplinks:appclips:json
{
"expo": {
"ios": {
"associatedDomains": [
"applinks:may20.expo.app",
"appclips:may20.expo.app"
]
}
}
}在中,声明Clip的权限:
targets/clip/expo-target.config.jsjs
/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = (config) => ({
type: "clip",
icon: "https://github.com/expo.png",
entitlements: {
"com.apple.developer.associated-domains": ["appclips:may20.expo.app"],
},
});如果跳过此步骤,会提示:expo prebuild(Apple App Clip可能需要关联域名权限,但未找到)。Apple App Clip may require the associated domains entitlement but none were found
4. Register bundle IDs and create the App Store entry
4. 注册Bundle ID并创建App Store条目
sh
bunx setup-safariThis logs in to the Apple Developer account, registers , creates the App Store Connect entry, and prints:
com.bacon.may20- A starter JSON
apple-app-site-association - A tag with the iTunes app id
<meta name="apple-itunes-app"> - Team ID, iTunes ID, and Bundle ID
sh
bunx setup-safari此命令会登录Apple开发者账户,注册,创建App Store Connect条目,并输出:
com.bacon.may20- 初始的JSON内容
apple-app-site-association - 包含iTunes应用ID的标签
<meta name="apple-itunes-app"> - 团队ID、iTunes ID和Bundle ID
5. Host the AASA file
5. 托管AASA文件
App Clips are invoked when iOS fetches and finds a matching entry.
https://<your-domain>/.well-known/apple-app-site-associationappclipssh
mkdir -p public/.well-known
touch public/.well-known/apple-app-site-associationPaste the JSON printed, but add an block for the Clip's full app ID (). The output of only covers the parent app:
setup-safariappclips<TeamID>.<ClipBundleID>setup-safarijson
{
"applinks": {
"details": [
{
"appIDs": ["XX57RJ5UTD.com.bacon.may20"],
"components": [{ "/": "*", "comment": "Matches all routes" }]
}
]
},
"appclips": {
"apps": ["XX57RJ5UTD.com.bacon.may20.clip"]
},
"activitycontinuation": {
"apps": ["XX57RJ5UTD.com.bacon.may20"]
},
"webcredentials": {
"apps": ["XX57RJ5UTD.com.bacon.may20"]
}
}Notes:
- The file has no extension and no requirements beyond being served as-is. Expo Router static export serves files in
Content-Typeverbatim.public/ - The block is what lets a URL on the domain launch the Clip.
appclips - is used for sharing credentials between the website, parent app, and the App Clip.
webcredentials - is optional and used for sharing the link between mobile and desktop. Must be used with
activitycontinuationfrom expo-router — see https://docs.expo.dev/router/advanced/apple-handoff/Head - Notation and route-disabling details: https://sosumi.ai/documentation/xcode/supporting-associated-domains
当iOS获取并找到匹配的条目时,会调用App Clip。
https://<your-domain>/.well-known/apple-app-site-associationappclipssh
mkdir -p public/.well-known
touch public/.well-known/apple-app-site-association粘贴输出的JSON内容,但需添加针对Clip完整应用ID()的块。的输出仅包含主应用的配置:
setup-safari<TeamID>.<ClipBundleID>appclipssetup-safarijson
{
"applinks": {
"details": [
{
"appIDs": ["XX57RJ5UTD.com.bacon.may20"],
"components": [{ "/": "*", "comment": "Matches all routes" }]
}
]
},
"appclips": {
"apps": ["XX57RJ5UTD.com.bacon.may20.clip"]
},
"activitycontinuation": {
"apps": ["XX57RJ5UTD.com.bacon.may20"]
},
"webcredentials": {
"apps": ["XX57RJ5UTD.com.bacon.may20"]
}
}注意事项:
- 该文件无扩展名,且除了原样托管外没有要求。Expo Router静态导出会原样托管
Content-Type目录下的文件。public/ - 块是让域名上的URL能够启动Clip的关键。
appclips - 用于在网站、主应用和App Clip之间共享凭据。
webcredentials - 是可选配置,用于在移动设备和桌面设备之间共享链接。必须与expo-router的
activitycontinuation组件配合使用——详情请查看https://docs.expo.dev/router/advanced/apple-handoff/Head - 符号说明和路由禁用细节:https://sosumi.ai/documentation/xcode/supporting-associated-domains
6. Add the Smart App Banner meta tag
6. 添加智能应用横幅元标签
Create (Expo Router's HTML shell) and add the tag from . Create the versioned template if it doesn't exist:
src/app/+html.tsxsetup-safarish
bunx expo customize src/app/+html.tsxAdd the meta tag to the :
<head>tsx
import { ScrollViewStyleReset } from "expo-router/html";
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-itunes-app" content="app-id=6771566491" />
<ScrollViewStyleReset />
</head>
<body>{children}</body>
</html>
);
}To make the website show the App Clip card instead of the install card, use:
html
<meta
name="apple-itunes-app"
content="app-id=6771566491, app-clip-bundle-id=com.bacon.may20.clip, app-clip-display=card"
/>创建(Expo Router的HTML外壳)并添加输出的标签。如果不存在版本化模板,请创建:
src/app/+html.tsxsetup-safarish
bunx expo customize src/app/+html.tsx在中添加元标签:
<head>tsx
import { ScrollViewStyleReset } from "expo-router/html";
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-itunes-app" content="app-id=6771566491" />
<ScrollViewStyleReset />
</head>
<body>{children}</body>
</html>
);
}若要让网站显示App Clip卡片而非安装卡片,请使用:
html
<meta
name="apple-itunes-app"
content="app-id=6771566491, app-clip-bundle-id=com.bacon.may20.clip, app-clip-display=card"
/>7. Deploy the website
7. 部署网站
The AASA file must be live before iOS will trust the association. Use EAS Hosting:
sh
bunx expo export -p web
eas deploy --prodThis publishes the site (including ) at . Verify:
/.well-known/apple-app-site-associationhttps://<slug>.expo.appsh
curl https://may20.expo.app/.well-known/apple-app-site-associationAASA文件必须处于在线状态,iOS才会信任该关联。使用EAS Hosting:
sh
bunx expo export -p web
eas deploy --prod此命令会将站点(包括)发布到。验证方式:
/.well-known/apple-app-site-associationhttps://<slug>.expo.appsh
curl https://may20.expo.app/.well-known/apple-app-site-association8. Mirror permissions
8. 镜像权限配置
Inspect the parent app's permissions after prebuild:
sh
npx expo config --type introspectLook at the object — mirror the permission keys in the App Clip's so matching APIs can be used from the Clip.
infoPlistInfo.plistSet in the Clip's target config — App Clips have a higher minimum size limit in iOS 17.6.
deploymentTarget: "17.6"If the app uses push notifications or location services, add to the App Clip's to request the necessary permissions:
Info.plistxml
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestEphemeralUserNotification</key>
<false/>
<key>NSAppClipRequestLocationConfirmation</key>
<true/>
</dict>预构建后检查主应用的权限:
sh
npx expo config --type introspect查看对象——在App Clip的中镜像相同的权限键,以便Clip可以使用匹配的API。
infoPlistInfo.plist在Clip的目标配置中设置——iOS 17.6中App Clip的最小大小限制更高。
deploymentTarget: "17.6"如果应用使用推送通知或定位服务,请在App Clip的中添加配置以请求必要的权限:
Info.plistxml
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestEphemeralUserNotification</key>
<false/>
<key>NSAppClipRequestLocationConfirmation</key>
<true/>
</dict>9. Build and submit to TestFlight
9. 构建并提交至TestFlight
sh
bunx testflightThis will:
- Generate an if missing.
eas.json - Set up credentials for both targets (parent + Clip). Each gets its own provisioning profile but can share a single Distribution Certificate.
- Sync capabilities — note for the Clip target.
Enabled: Associated Domains - Build, upload, and schedule a TestFlight submission.
sh
bunx testflight此命令会:
- 如果缺少则生成该文件。
eas.json - 为两个目标(主应用+Clip)设置凭据。每个目标都有自己的配置文件,但可以共享同一个分发证书。
- 同步功能——注意Clip目标的“已启用:关联域名”。
- 构建、上传并安排TestFlight提交。
10. Configure App Clip metadata
10. 配置App Clip元数据
Pull existing App Store metadata to local:
sh
eas metadata:pullAdd to . Up to 3 invocation URLs can launch the Clip from a web page:
apple.appClipstore.config.jsonjson
{
"configVersion": 0,
"apple": {
"appClip": {
"defaultExperience": {
"action": "PLAY",
"releaseWithAppStoreVersion": true,
"reviewDetail": {
"invocationUrls": ["https://may20.expo.app/", null, null]
},
"info": {
"en-US": {
"subtitle": "Instantly native with Expo",
"headerImage": "store/apple/app-clip/en-US/asc-app-clip.png"
}
}
}
}
}
}The must be a 1800x1200 PNG with no opacity.
headerImagePush back to the store:
sh
eas metadata:pushApple's recommended App Clip metadata guidelines: https://sosumi.ai/documentation/appclip/configuring-the-launch-experience-of-your-app-clip
将现有App Store元数据拉取到本地:
sh
eas metadata:pull在中添加配置。最多可设置3个调用URL,用于从网页启动Clip:
store.config.jsonapple.appClipjson
{
"configVersion": 0,
"apple": {
"appClip": {
"defaultExperience": {
"action": "PLAY",
"releaseWithAppStoreVersion": true,
"reviewDetail": {
"invocationUrls": ["https://may20.expo.app/", null, null]
},
"info": {
"en-US": {
"subtitle": "Instantly native with Expo",
"headerImage": "store/apple/app-clip/en-US/asc-app-clip.png"
}
}
}
}
}
}headerImage将元数据推送回商店:
sh
eas metadata:pushWhat you get
你将获得
- Parent app target:
com.bacon.may20 - App Clip target: , lives in
com.bacon.may20.cliptargets/clip/ - AASA hosted at
https://may20.expo.app/.well-known/apple-app-site-association - Smart App Banner meta tag on every web route
- Every route linked to its native counterpart
- TestFlight build of the parent app with the Clip embedded
Once Apple invokes the Clip from a URL on the domain, iOS opens 's entry point which loads the React Native app.
targets/clip/- 主应用目标:
com.bacon.may20 - App Clip目标:,存放在
com.bacon.may20.clip目录中targets/clip/ - 托管在的AASA文件
https://may20.expo.app/.well-known/apple-app-site-association - 每个网页路由上的智能应用横幅元标签
- 每个路由都链接到对应的原生版本
- 包含Clip的主应用TestFlight构建
一旦Apple通过域名上的URL调用Clip,iOS会打开的入口点,加载React Native应用。
targets/clip/Native detection (optional)
原生检测(可选)
To let JS detect when it's running inside an App Clip and present an install prompt for the full app, create a local Expo module () that exposes .
bunx create-expo-module --localnavigator.appClip.prompt()See ./references/native-module.md for the Swift module, TypeScript interface, and usage.
若要让JS检测是否在App Clip中运行,并显示主应用的安装提示,请创建一个本地Expo模块(),暴露方法。
bunx create-expo-module --localnavigator.appClip.prompt()详情请查看./references/native-module.md中的Swift模块、TypeScript接口及使用方法。
References
参考资料
- ./references/native-module.md — Local Expo module to detect App Clip context and present the SKOverlay install prompt
- ./references/native-module.md —— 用于检测App Clip上下文并显示SKOverlay安装提示的本地Expo模块