add-app-clip

Par expo · skills

Ajouter une cible App Clip iOS à une application Expo. À utiliser lorsque l'utilisateur mentionne App Clip, AASA, apple-app-site-association, appclips, smart app banner, ou souhaite distribuer un Clip iOS léger déclenché depuis une URL aux côtés de son application parente.

npx skills add https://github.com/expo/skills --skill add-app-clip

Ajouter un App Clip à une application Expo

Ajoute une cible iOS App Clip à un projet Expo. Le Clip réside dans targets/clip/, est distribué avec l'application parent, et est invoqué à partir d'une URL du domaine de l'application via un fichier Apple App Site Association (AASA).

L'identifiant de bundle de l'application parent devient com.<username>.<app-name> et celui du Clip est automatiquement dérivé comme <parent>.clip (ex. com.bacon.may20.clip).

1. Définis bundleIdentifier et appleTeamId

bun create target avertit si ces paramètres manquent. Ajoute à app.json :

{
  "expo": {
    "ios": {
      "bundleIdentifier": "com.<username>.<app-name>",
      "appleTeamId": "XX57RJ5UTD"
    }
  }
}

2. Ajoute la cible App Clip

bun create target clip

Cela installe @bacons/apple-targets, l'ajoute au tableau plugins dans app.json, et crée :

  • targets/clip/expo-target.config.js — le plugin de configuration de la cible
  • targets/clip/Info.plist — Info.plist du Clip
  • targets/clip/AppDelegate.swift, Assets.xcassets, etc.

Choisis une bonne icône ou réutilise celle existante définie dans l'application — vérifie-la avec bunx expo config sous la clé icon ou ios.icon.

3. Configure les domaines associés

L'application parent et le Clip ont tous deux besoin de l'entitlement Associated Domains pointant vers le domaine qui héberge le fichier AASA.

Dans app.json, ajoute à la fois 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éclare l'entitlement 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 tu passes cette étape, expo prebuild affichera : Apple App Clip may require the associated domains entitlement but none were found.

4. Enregistre les identifiants de bundle et crée 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-association de démarrage
  • Une balise <meta name="apple-itunes-app"> avec l'ID iTunes de l'application
  • Team ID, iTunes ID, et Bundle ID

5. Héberge 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

Colle le JSON que setup-safari a affiché, mais ajoute un bloc appclips pour l'ID complet de l'application Clip (<TeamID>.<ClipBundleID>). La sortie de setup-safari couvre uniquement 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 de Content-Type au-delà d'être servi tel quel. L'export statique Expo Router sert les fichiers dans public/ verbatim.
  • Le bloc appclips est ce qui permet à une URL du domaine de lancer le Clip.
  • webcredentials est utilisé pour partager les identifiants entre le site, l'application parent et l'App Clip.
  • activitycontinuation est optionnel et utilisé pour partager le lien entre mobile et desktop. Doit être utilisé avec Head depuis expo-router — voir https://docs.expo.dev/router/advanced/apple-handoff/
  • Notation et détails de désactivation des routes : https://sosumi.ai/documentation/xcode/supporting-associated-domains

6. Ajoute la balise Smart App Banner meta

Crée src/app/+html.tsx (le shell HTML d'Expo Router) et ajoute la balise de setup-safari. Crée le modèle versionné s'il n'existe pas :

bunx expo customize src/app/+html.tsx

Ajoute 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 que le site affiche la carte App Clip au lieu de la carte d'installation, utilise :

<meta
  name="apple-itunes-app"
  content="app-id=6771566491, app-clip-bundle-id=com.bacon.may20.clip, app-clip-display=card"
/>

7. Déploie le site

Le fichier AASA doit être en ligne avant qu'iOS ne fasse confiance à l'association. Utilise 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érifie :

curl https://may20.expo.app/.well-known/apple-app-site-association

8. Miroir les permissions

Inspecte les permissions de l'application parent après prebuild :

npx expo config --type introspect

Regardes l'objet infoPlist — miroir les clés de permission dans le Info.plist du App Clip afin que les APIs correspondantes puissent être utilisées depuis le Clip.

Définis 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, ajoute au 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. Build et soumets à TestFlight

bunx testflight

Cela va :

  1. Générer un eas.json s'il manque.
  2. Configurer les identifiants pour les deux cibles (parent + Clip). Chacune obtient son propre profil de provisionnement mais peuvent partager un seul certificat de distribution.
  3. Synchroniser les capacités — note Enabled: Associated Domains pour la cible Clip.
  4. Build, télécharger, et programmer une soumission TestFlight.

10. Configure les métadonnées App Clip

Récupère les métadonnées App Store existantes en local :

eas metadata:pull

Ajoute 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"
          }
        }
      }
    }
  }
}

Le headerImage doit être un PNG 1800×1200 sans opacité.

Renvoie les changements au store :

eas metadata:push

Directives de métadonnées App Clip recommandées par Apple : https://sosumi.ai/documentation/appclip/configuring-the-launch-experience-of-your-app-clip

Ce que tu obtiens

  • Cible d'application parent : com.bacon.may20
  • Cible App Clip : com.bacon.may20.clip, réside dans targets/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 embarqué

Une fois qu'Apple invoque le Clip à partir d'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 permettre à JS de détecter quand il s'exécute à l'intérieur d'un App Clip et présenter une invite d'installation pour l'application complète, crée 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 App Clip et présenter l'invite d'installation SKOverlay

Skills similaires