Los Universal Links en iOS y los App Links en Android requieren un archivo JSON servido por HTTPS en la raíz de tu dominio. iOS quiere /.well-known/apple-app-site-association. Android quiere /.well-known/assetlinks.json. localhost no puede servir ninguno sobre un hostname que un SO asocie con tu app. Así que tunelizamos.
El requisito real en cada plataforma
iOS Universal Links
El SO obtiene https://tu-dominio.com/.well-known/apple-app-site-association (o /apple-app-site-association) cuando tu app se instala. Compara el contenido contra el entitlement de dominios asociados de tu app. La obtención debe tener éxito sobre HTTPS real. iOS no confía en certificados autofirmados para esto, ni en localhost.
Android App Links
Android verifica los App Links obteniendo https://tu-dominio.com/.well-known/assetlinks.json. El archivo debe listar los fingerprints SHA-256 de tu app. La verificación ocurre al instalar y en el primer lanzamiento. La misma restricción: HTTPS real, dominio real.
Por qué localhost no puede hacerlo directamente
Incluso con mkcert dándote HTTPS confiable en el navegador de tu portátil, el SO móvil no confía en tu CA local. Y el dominio en tu apple-app-site-association tiene que coincidir con el dominio asociado en el entitlement de la app — que es un dominio real resoluble por DNS, no localhost.
Algunos equipos sortean esto con un dominio de staging dedicado. Funciona, pero el bucle de iteración es lento. Un túnel localhost te da una URL HTTPS real en un subdominio real que los dispositivos móviles alcanzan sin quejarse.
Cómo conectarlo
- Sirve tu
apple-app-site-associationyassetlinks.jsondesde tu servidor de desarrollo local en/.well-known/. Ambos archivos. Ambas rutas. - Ejecuta
npx portpreview 3000(o el puerto que use tu servidor de desarrollo). - Anota el hostname del túnel — algo como
abc123.portpreview.dev. - En el entitlement de dominios asociados de tu app iOS, añade
applinks:abc123.portpreview.dev. En el intent filter de Android, añade el mismo host. - Compila e instala la app en un dispositivo real (los simuladores tienen un comportamiento raro con Universal Links).
- Abre la URL desde otra app — Notas, Mail, un código QR — y mira cómo enruta a tu app instalada en vez del navegador.
El truco al tunelizar para deep links: el hostname del túnel cambia entre sesiones a menos que tengas un subdominio reservado. Cada rotación significa recompilar la app con la nueva entrada de dominio asociado. Si iteras a menudo en el comportamiento de deep links, consigue un subdominio reservado.
El content-type importa para apple-app-site-association
iOS espera el archivo sin extensión y con content-type application/json (iOS más nuevo) o application/pkcs7-mime (formato antiguo firmado). Casi todas las apps modernas usan la variante JSON plana. Asegúrate de que tu servidor de desarrollo devuelva el content-type correcto o iOS rechaza el archivo en silencio sin error útil.
Prueba primero desde un navegador de escritorio: abre https://tu-tunel.portpreview.dev/.well-known/apple-app-site-association y confirma que el JSON se renderiza y la cabecera content-type en dev tools es correcta. Si está mal, arréglalo antes de perseguir bugs de Universal Link en el simulador.
Las otras trampas de depuración de deep links
Desajuste de entitlement de la app
Si tu entitlement de dominios asociados dice applinks:abc.portpreview.dev pero tu apple-app-site-association lista def.portpreview.dev, iOS no obtiene nada. El hostname debe ser consistente.
Universal Link desde dentro de Safari
Tocar un Universal Link dentro de Safari (la misma app donde está la pestaña) a veces abre en Safari en vez de la app. Es por diseño — iOS evita el truco de "abrir app cada vez que el usuario hace clic en un enlace". Prueba desde Notas o Mail.
Android App Links y digital asset links
Android también admite esquemas de URL personalizados (tuapp://ruta), que no necesitan HTTPS ni assetlinks.json. Son más fáciles de probar pero menos seguros — cualquier app puede registrar el mismo esquema. Para deep linking con calidad de producción, los App Links son la respuesta.
El flujo de pruebas móviles que usamos
Para un proyecto que entrega iOS y Android con deep links:
- Arranca el backend que sirve los archivos
.well-known. - Tunelízalo con
npx portpreview 3000. - Una vez la URL del túnel sea estable para la sesión (o usa un subdominio reservado), actualiza las entradas de dominio asociado de la app y compila.
- QA en dispositivos físicos — tanto instalaciones nuevas como actualizaciones.
- Para combos de OAuth-y-deep-link (proveedores de inicio de sesión que redirigen de vuelta a la app), combínalo con pruebas de callbacks OAuth.
La configuración es molesta la primera vez. Después, la URL del túnel es solo otra variable de entorno en el scheme de Xcode y Gradle.
Para patrones más amplios de pruebas móviles, mira pruebas móviles con un túnel localhost. Únete a la lista de espera de PortPreview.