eduardweb.
Navigație & StateAvansat#react-native#android#ios#deep-linking#expo-router

Deep linking cu Expo Router: trecerea de la custom schemes la Universal Links fără nervi

De Paul Ene, 12 aug. 2026 · 4 vizualizări · 2 like-uri

Postat acum 4 zile
typescript
import { useLocalSearchParams, Stack } from 'expo-router';
import { Text, View, ActivityIndicator } from 'react-native';

export default function PayScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();

  if (!id) {
    return <ActivityIndicator />;
  }

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Stack.Screen options={{ title: `Tranzacție #${id}` }} />
      <Text style={{ fontSize: 18, fontWeight: 'bold' }}>
        Procesăm plata pentru comanda: {id}
      </Text>
    </View>
  );
}

Am avut recent de implementat flow-ul de autentificare prin Magic Link pentru o aplicație de fintech cu vreo 45k useri activi. Dacă pe custom schemes (myapp://) totul a mers în 10 minute, când am trecut la Universal Links (iOS) și App Links (Android) cu Expo Router v3, am dat peste toate capcanele posibile.

Dacă vrei ca link-ul https://app.domeniu.ro/pay/123 să deschidă direct ecranul nativ fără să mai treacă prin Safari sau Chrome, postul ăsta e pentru tine.

De la Custom Schemes la Universal Links

Să fim sinceri: custom scheme-ul (myapp://) e ridicol de simplu de configurat în Expo. Pui "scheme": "myapp" în app.json și gata. Totul funcționează perfect în development. Problema? Orice altă aplicație instalată pe telefon poate înregistra același scheme. Dacă un atacator își face o aplicație cu myapp://, îți poate intercepta jetoanele de autentificare din URL fără nicio restricție.

Pentru producție, mai ales în fintech sau e-commerce, ai nevoie de domain verification. Asta înseamnă Universal Links pe iOS și App Links pe Android.

Configurarea corectă în app.json

Expo Router abstractizează mult din configurarea nativă, dar trebuie totuși să declari domeniile în setările de build nativ. Punctul critic pe iOS este associatedDomains, iar pe Android este lista de intentFilters cu autoVerify: true.

Dacă uiți de autoVerify: true pe Android, sistemul de operare nu va verifica domeniul la instalarea aplicației și va deschide link-ul în browser în loc să deschidă aplicația nativă.

Coșmarul numit AASA și Apple CDN

Aici se blochează 90% din developeri. Pentru iOS, trebuie să servești fișierul apple-app-site-association la https://domeniu.ro/.well-known/apple-app-site-association.

Am pierdut vreo 6 ore încercând să înțeleg de ce link-ul meu nu se deschidea pe un iPhone fizic, deși pe simulator mergea brici. Apple NU verifică serverul tău direct de fiecare dată când instalezi aplicația; folosește un CDN propriu care face cache agresiv (până la 24h).

Două lucruri esențiale pe care le-am învățat pe pielea mea:

  1. Header-ul de Content-Type trebuie să fie neapărat application/json (fără extensie .json în URL-ul servit!).
  2. Dacă folosești Cloudflare, dezactivează Bot Management pe ruta .well-known, altfel robotul Applebot primește 403 când încearcă să valideze domeniul.

Rutarea automată în Expo Router

Aici excelează Expo Router. Nu mai trebuie să scrii Linking.addEventListener manual și să faci parsare complicată cu regex așa cum făceam în React Navigation 5. Structura ta de fișiere din folderul app/ este chiar harta URL-urilor.

Dacă fișierul tău se află în app/pay/[id].tsx, un link deschis din e-mail de forma https://app.domeniu.ro/pay/9921 va fi capturat direct de router, iar id-ul va fi extras automat în hook-ul useLocalSearchParams().

Concluzie și trade-off-uri

Universal Links oferă o experiență impecabilă și securitate maximă, dar setup-ul inițial e lent din cauza cache-ului Apple și cerințelor rigide de HTTPS. Custom schemes sunt bune doar pentru dev build-uri și teste rapide pe staging.

Voi ce soluție folosiți pentru debug pe iOS când Apple refuză să facă re-fetch la AASA? Porniți modul developer pe dispozitiv sau treceți prin proxy local?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.