Ajouter un App Clip à une application Expo
Prérequis. L'ajout de la cible App Clip est open source. Son déploiement nécessite une adhésion à l'Apple Developer Program, un examen App Store, et le fichier AASA doit être servi en HTTPS sur votre domaine (n'importe quel hôte HTTPS fonctionne ; EAS Hosting en est une option). La compilation via EAS Build ou
bunx testflightutilise les minutes de compilation de votre plan EAS. Voir https://expo.dev/pricing et https://developer.apple.com/app-clips/.
Ajoute une cible iOS App Clip à un projet Expo. Le Clip réside dans targets/clip/, est livré avec l'application parent, et est invoqué via une URL du domaine de l'application à travers un fichier Apple App Site Association (AASA).
L'identifiant du bundle de l'application parent devient com.<username>.<app-name> et celui du Clip est automatiquement dérivé en <parent>.clip (par ex. com.bacon.may20.clip).
1. Définir bundleIdentifier et appleTeamId
bun create target avertit si ceux-ci manquent. Ajoutez à app.json :
{
"expo": {
"ios": {
"bundleIdentifier": "com.<username>.<app-name>",
"appleTeamId": "XX57RJ5UTD"
}
}
}
2. Ajouter la cible App Clip
bun create target clip
Cela installe @bacons/apple-targets, l'ajoute au tableau plugins dans app.json, et écrit :
targets/clip/expo-target.config.js— le plugin de configuration de la cibletargets/clip/Info.plist— Info.plist du Cliptargets/clip/AppDelegate.swift,Assets.xcassets, etc.
Choisissez une bonne icône ou réutilisez celle existante définie dans l'application — vérifiez-la avec bunx expo config sous la clé icon ou ios.icon.
3. Configurer les domaines associés
L'application parent et le Clip ont besoin du droit Associated Domains pointant vers le domaine qui héberge le fichier AASA.
Dans app.json, ajoutez les entrées applinks: (parent) et appclips: (invocation du Clip) :
{
"expo": {
"ios": {
"associatedDomains": [
"applinks:may20.expo.app",
"appclips:may20.expo.app"
]
}
}
}
Dans targets/clip/expo-target.config.js, déclarez le droit du Clip :
/** @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"],
},
});
Si vous sautez cette étape,
expo prebuildaffichera :Apple App Clip may require the associated domains entitlement but none were found.
4. Enregistrer les identifiants de bundle et créer l'entrée App Store
bunx setup-safari
Cela se connecte au compte Apple Developer, enregistre com.bacon.may20, crée l'entrée App Store Connect, et affiche :
- Un JSON
apple-app-site-associationde démarrage - Une balise
<meta name="apple-itunes-app">avec l'identifiant iTunes - ID de l'équipe, ID iTunes et ID de bundle
5. Héberger le fichier AASA
Les App Clips sont invoqués quand iOS récupère https://<your-domain>/.well-known/apple-app-site-association et trouve une entrée appclips correspondante.
mkdir -p public/.well-known
touch public/.well-known/apple-app-site-association
Collez le JSON que setup-safari a affiché, mais ajoutez un bloc appclips pour l'ID d'application complet du Clip (<TeamID>.<ClipBundleID>). La sortie de setup-safari ne couvre que l'application parent :
{
"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 :
- Le fichier n'a pas d'extension et aucune exigence
Content-Typeau-delà d'être servi tel quel. L'export statique Expo Router sert les fichiers danspublic/tels quels. - Le bloc
appclipspermet à une URL du domaine de lancer le Clip. webcredentialsest utilisé pour partager les identifiants entre le site web, l'application parent et l'App Clip.activitycontinuationest optionnel et utilisé pour partager le lien entre mobile et bureau. Doit être utilisé avecHeadd'expo-router — voir https://docs.expo.dev/router/advanced/apple-handoff/- Notation et détails de désactivation de route : https://sosumi.ai/documentation/xcode/supporting-associated-domains
6. Ajouter la balise Smart App Banner meta
Créez src/app/+html.tsx (shell HTML d'Expo Router) et ajoutez la balise de setup-safari. Créez le modèle versionné s'il n'existe pas :
bunx expo customize src/app/+html.tsx
Ajoutez la balise meta au <head> :
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>
);
}
Pour faire afficher la carte App Clip au lieu de la carte d'installation sur le site web, utilisez :
<meta
name="apple-itunes-app"
content="app-id=6771566491, app-clip-bundle-id=com.bacon.may20.clip, app-clip-display=card"
/>
7. Déployer le site web
Le fichier AASA doit être en ligne avant qu'iOS ne fasse confiance à l'association. Utilisez EAS Hosting :
bunx expo export -p web
eas deploy --prod
Cela publie le site (y compris /.well-known/apple-app-site-association) à https://<slug>.expo.app. Vérifiez :
curl https://may20.expo.app/.well-known/apple-app-site-association
8. Dupliquer les permissions
Inspectez les permissions de l'application parent après prebuild :
npx expo config --type introspect
Regardez l'objet infoPlist — dupliquez les clés de permission dans Info.plist du App Clip pour que les API correspondantes puissent être utilisées depuis le Clip.
Définissez deploymentTarget: "17.6" dans la configuration cible du Clip — les App Clips ont une limite de taille minimale plus élevée dans iOS 17.6.
Si l'application utilise les notifications push ou les services de localisation, ajoutez à Info.plist du App Clip pour demander les permissions nécessaires :
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestEphemeralUserNotification</key>
<false/>
<key>NSAppClipRequestLocationConfirmation</key>
<true/>
</dict>
9. Compiler et soumettre à TestFlight
bunx testflight
Cela va :
- Générer un
eas.jsons'il manque. - Configurer les identifiants pour les deux cibles (parent + Clip). Chacun obtient son propre profil de provisioning mais peut partager un seul certificat de distribution.
- Synchroniser les capacités — notez
Enabled: Associated Domainspour la cible Clip. - Compiler, télécharger et planifier une soumission TestFlight.
10. Configurer les métadonnées d'App Clip
Extraire les métadonnées d'App Store existantes en local :
eas metadata:pull
Ajoutez apple.appClip à store.config.json. Jusqu'à 3 URL d'invocation peuvent lancer le Clip depuis une page web :
{
"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 doit être un PNG 1800x1200 sans transparence.
Repoussez vers le magasin :
eas metadata:push
Recommandations d'Apple pour les métadonnées d'App Clip : https://sosumi.ai/documentation/appclip/configuring-the-launch-experience-of-your-app-clip
Ce que vous obtenez
- Cible application parent :
com.bacon.may20 - Cible App Clip :
com.bacon.may20.clip, réside danstargets/clip/ - AASA hébergé à
https://may20.expo.app/.well-known/apple-app-site-association - Balise Smart App Banner meta sur chaque route web
- Chaque route liée à son équivalent natif
- Build TestFlight de l'application parent avec le Clip intégré
Une fois qu'Apple invoque le Clip depuis une URL du domaine, iOS ouvre le point d'entrée de targets/clip/ qui charge l'application React Native.
Détection native (optionnel)
Pour laisser JS détecter quand il s'exécute dans un App Clip et présenter une invite d'installation pour l'application complète, créez un module Expo local (bunx create-expo-module --local) qui expose navigator.appClip.prompt().
Voir ./references/native-module.md pour le module Swift, l'interface TypeScript et l'utilisation.
Références
- ./references/native-module.md — Module Expo local pour détecter le contexte d'App Clip et présenter l'invite d'installation SKOverlay