{
"expo": {
"name": "ShopApp",
"slug": "shop-app",
"scheme": "shopapp",
"ios": {
"bundleIdentifier": "com.shopapp.app",
"associatedDomains": ["applinks:shopapp.com"]
},
"android": {
"package": "com.shopapp.app",
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "shopapp.com",
"pathPrefix": "/produs"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
},
"plugins": ["expo-router"]
}
}Salutare! Am configurat recent deep linking hibrid (Custom Schemes + Universal Links) pe un proiect cu Expo Router unde aveam în jur de 15k utilizatori activi zilnic. Dacă ai încercat vreodată să legi domeniile de web cu pachetele native fără să rupi navigarea internă, știi ce bătaie de cap poate fi. În postarea asta trecem prin pașii reali și greșelile pe care le-am făcut noi ca să nu pierzi două zile la debugging.
De la custom scheme la Universal Links
Custom schemes (myapp://produs/123) sunt super simplu de pus la punct, dar pe iOS au un dezavantaj imens. Dacă aplicația nu e instalată pe telefonul clientului, Safari afișează o eroare urâtă direct în față: "Safari cannot open the page because the address is invalid". Clientul nostru a strâmbat din nas, așa că a trebuit să trecem de la schemă simplă la Universal Links (https://myapp.com/produs/123).
Expo Router a simplificat enorm partea de JS și TypeScript. Datorită sistemului bazat pe fișiere (file-based routing), dacă ai structura app/produs/[id].tsx, cadrul mapează automat URL-ul nativ exact ca pe web. Nu mai trebuie să scrii manual mapping-uri uriașe în React Navigation cu linking.config cum făceam acum trei ani. Marea problemă nu mai e în codul de React Native, ci la nivel de OS și verificare de domenii.
Fișierele magice: AASA și assetlinks.json
Aici am pierdut cel mai mult timp la proiect. Pe iOS, Apple cere un fișier numit apple-app-site-association (AASA) servit pe HTTPS la root sau în .well-known/.
Am învățat pe pielea noastră trei reguli stricte pentru Apple:
- Fișierul nu trebuie să aibă extensia
.jsonîn URL, deși conținutul e JSON. - Header-ul de răspuns trebuie să fie obligatoriu
content-type: application/json. - Apple urăște redirectările HTTP. Dacă serverul tău face 301 din non-www în www, iOS refuză silent să valideze legătura.
Pe Android lucrurile sunt mai iertătoare cu assetlinks.json, dar există o capcană: dacă greșești SHA-256 fingerprint-ul certificatului de release din Google Play Console (atenție, cel de App Signing, nu cel cu care semnezi tu local!), Android ignoră direct linkul și deschide browserul fără nicio eroare în logcat.
Configurația din app.json
Un trade-off sincer la Expo Router: este extrem de comod că generăm configurația nativă prin Prebuild, dar când iOS refuză să descarce fișierul AASA din cauza CDN-ului Apple (care face cache agresiv până la 24-48 de ore pe producție), ești legat de mâini și de picioare. Nu există un buton de "force refresh" în Apple Developer Account.
În app.json, trebuie setat domeniul la associatedDomains pentru iOS și definit intentFilters pentru Android. Când rulezi npx expo prebuild, Expo injectează automat entitlements în Xcode și intent-urile în AndroidManifest.xml.
Pentru testare locală, cel mai bun prieten al meu a fost CLI-ul uri-scheme. Trimiți un eveniment direct în simulator fără să stai să dai sms-uri sau mailuri cu linkul.
Voi ce workflow folosiți când aveți de testat Universal Links în staging? Folosiți un proxy local cu ngrok sau urcați AASA direct pe un domeniu separat de dev?