Enlaces Universales de iOS
Dominios asociados, apple-app-site-association y hospedaje en CDN - la guía de iOS para abrir URLs https:// de marketing directamente en tu app Expo SDK 57 sin un diálogo de desambiguación de esquema personalizado.
Busca en todas las páginas de la documentación
Dominios asociados, apple-app-site-association y hospedaje en CDN - la guía de iOS para abrir URLs https:// de marketing directamente en tu app Expo SDK 57 sin un diálogo de desambiguación de esquema personalizado.
Tarjeta de referencia rápida - lista para copiar y pegar.
// app.config.ts
export default {
expo: {
scheme: "shopapp",
ios: {
bundleIdentifier: "com.example.shopapp",
associatedDomains: ["applinks:shop.example.com"],
},
},
};// https://shop.example.com/.well-known/apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAMID.com.example.shopapp",
"paths": ["/orders/*", "/product/*", "/invite/*"]
}
]
}
}Cuándo usarlo:
https:// que deberían abrir la app cuando está instalada.myapp://.Configuración de extremo a extremo para https://shop.example.com/orders/* en una app Expo con prebuild.
Paso 1 - Derechos a través de app.config.ts
// app.config.ts
import type { ExpoConfig } from "expo/config";
const config: ExpoConfig = {
name: "ShopApp",
slug: "shop-app",
scheme: "shopapp",
ios: {
bundleIdentifier: "com.example.shopapp",
associatedDomains: ["applinks:shop.example.com"],
},
android: {
package: "com.example.shopapp",
},
};
export default config;Paso 2 - Hospeda AASA en tu CDN
{
"applinks": {
"apps": [],
"details": [
{
"appID": "ABCDE12345.com.example.shopapp",
"paths": [
"/orders/*",
"/product/*",
"NOT /admin/*",
"NOT /api/*"
]
}
]
},
"webcredentials": {
"apps": ["ABCDE12345.com.example.shopapp"]
}
}Sirve en ambas rutas (Apple revisa una u otra):
https://shop.example.com/apple-app-site-associationhttps://shop.example.com/.well-known/apple-app-site-associationPaso 3 - Encabezados de respuesta de CDN
Content-Type: application/json
Cache-Control: max-age=3600.json; cuerpo JSON sin procesar.ABCDE12345 con tu Team ID de Apple del portal de desarrolladores.Paso 4 - Pantalla de Expo Router
// app/orders/[id].tsx
import { useLocalSearchParams } from "expo-router";
import { Text, View } from "react-native";
export default function OrderScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<View style={{ padding: 16 }}>
<Text>Order {id}</Text>
</View>
);
}Paso 5 - Construye y verifica
npx expo prebuild --platform ios
npx expo run:ios
# Simulador: abre el enlace universal
xcrun simctl openurl booted "https://shop.example.com/orders/42"Lo que esto demuestra:
/orders/42.applinks: en tus derechos.https:// coincidente, iOS abre tu app y pasa la URL a React Native.| Patrón | Coincide |
|---|---|
/orders/* | /orders/42, /orders/42/receipt |
/product/* | /product/widget |
NOT /admin/* | Excluye URLs de admin incluso si el padre coincidiría |
* | Todo en el dominio - evita a menos que la app sea propietaria del host completo |
| Verificación | Criterios de Aprobación |
|---|---|
| TLS | Certificado válido; sin http:// mezclado |
| Redirecciones | La URL de AASA devuelve 200 directamente |
| Content-Type | application/json o application/pkcs7-mime (firmado) |
| Tamaño | Menos de 128 KB |
| Accesibilidad | Internet público - sin VPN ni lista blanca de IP |
# Valida AASA con el CDN de Apple (el dispositivo eventualmente verá los mismos datos)
curl -s "https://app-site-association.cdn-apple.com/a/v1/shop.example.com" | jq .appID exactamente.TEAMID.bundleIdentifier de Apple Developer - Membership.www a apex rompe la obtención. Solución: sirve AASA en el host exacto en associatedDomains.eas build --platform ios después del cambio de configuración./api/* en rutas - Las respuestas JSON de API no deberían abrir la app. Solución: reglas NOT explícitas o prefijos más estrechos.SFSafariViewController o <a target="_blank"> para abrir externamente.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Solo esquema personalizado | Herramientas internas, redirecciones OAuth | Campañas de correo de consumidor |
| Firebase Dynamic Links (deprecado) | Proyectos heredados ya integrados | Nuevo - Google cerró en agosto de 2025 |
| Branch.io / AppsFlyer OneLink | Enlace profundo diferido + atribución | Los enlaces verificados simples son suficientes |
| Solo Smart App Banner | Empuje web a app sin enlaces universales completos | Necesitas toque para abrir sin Safari |
TEAMID.com.example.shopapp - sin espacios.details con diferentes valores appID para Bundle IDs de dev y prod.applinks:staging.example.com vs applinks:shop.example.com.curl -I para redirecciones y tipo de contenido.applinks:*.example.com en derechos y details separados por subdominio en AASA.*.example.com en rutas no es válido - lista cada subdominio explícitamente./orders/42?ref=email funciona si /orders/* coincide; la consulta no afecta las reglas de ruta.details - iOS elige la app instalada que coincide con appID.https://shop.example.com/orders/42 a app/orders/[id].tsx automáticamente cuando el esquema/host se configuran.Versiones de stack: Esta página se escribió para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 19 jul 2026