{
"expo": {
"scheme": "magazinulmeu",
"ios": {
"bundleIdentifier": "com.magazinulmeu.app",
"associatedDomains": ["applinks:magazinulmeu.ro"]
},
"android": {
"package": "com.magazinulmeu.app",
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "magazinulmeu.ro",
"pathPrefix": "/produs"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
}
}
}Am implementat deep linking-ul clasic acum vreo 6 ani în React Native cu vechiul react-navigation și țin minte că am vrut să-mi dau demisia direct. Recent, la un proiect de e-commerce cu vreo 12k useri activi unde trimiteam mii de notificări push pe zi, a trebuit să facem trecerea de la schemele custom (app://) la Universal Links (iOS) și App Links (Android). Cu Expo Router v3, structura bazată pe fișiere ajută enorm, dar configurarea nativă tot îți poate întinde nervii la maximum.
Diferența care doare: Custom Schemes vs. Universal Links
Să fim sinceri. O schemă de tipul magazinulmeu://produs/12 este super simplu de pus la punct. O trântești în app.json sub cheia scheme și gata, funcționează. Dar are un mare minus: dacă userul nu are aplicația instalată, link-ul e mort. Browserul va arăta o eroare urâtă de tipul "URL invalid".
Universal Links (iOS) și App Links (Android) folosesc URL-uri normale de HTTPS (ex: https://magazinulmeu.ro/produs/12). Dacă omul are aplicația, se deschide direct în ecranul corect. Dacă nu, se deschide în browser unde poți să-i arăți un landing page drăguț cu un buton de "Descarcă din App Store".
Trade-off-ul este evident: custom schemes le configurezi în 5 minute, dar arată neprofesionist. Pentru Universal Links ai nevoie de acces la domeniul web, certificate SSL, configurări de DNS și de un server web care să servească niște fișiere JSON specifice.
Configurația din app.json
Ca să nu scrii cod nativ în Xcode sau Android Studio, ne bazăm pe config plugin-urile din Expo. În app.json, trebuie să legăm domeniul nostru de aplicație nativă.
Pe iOS, associatedDomains trebuie să înceapă obligatoriu cu prefixul applinks:. Pe Android, intentFilters cu autoVerify: true îi spun sistemului de operare că aplicația ta dorește să devină handler-ul implicit pentru domeniul respectiv. Dacă omiți autoVerify, Android-ul va deschide în continuare link-ul în Chrome, fără să întrebe nimic.
Fișierele de pe server (AASA și AssetLinks)
Aici se blochează majoritatea developerilor. iOS și Android nu au încredere în aplicația ta doar pentru că ai scris tu un domeniu în app.json. Ele vor verifica domeniul respectiv ca să vadă dacă dețineți acea pagină web și dacă permiteți asocierea.
Pentru iOS, trebuie să urci un fișier numit apple-app-site-association (fără nicio extensie, NU .json) în folderul .well-known/ de pe serverul tău.
Pentru Android, pui assetlinks.json tot în .well-known/.
Am pățit o fază extrem de enervantă la proiectul de e-commerce: Apple își cache-uiește aceste fișiere pe serverele lor CDN. Când făceam teste pe un telefon fizic, modificam fișierul pe serverul nostru, dar pe telefon nu se întâmpla nimic pentru că iOS interoga CDN-ul Apple care avea versiunea veche. Ca să eviți asta în development, poți forța modul de bypass adăugând ?mode=developer la asocierea domeniului în Xcode sau folosind un simulator curat.
Cum gestionăm starea de auth?
Aici apare adevărata problemă de UX. Userul dă click pe link, aplicația se deschide direct pe ecranul /produs/12, dar el nu este logat. În Expo Router, dacă ai un middleware sau un layout care verifică sesiunea pe rutele protejate, s-ar putea să-l redirecționezi brutal către /login. După ce se loghează, cum îl trimiți înapoi la produsul pe care dăduse click?
Noi am rezolvat asta salvând URL-ul de deep link într-un state global (sau în AsyncStorage ca fallback) în momentul în care detectăm că userul e neautentificat. Apoi, după succesul login-ului, facem un router.replace(savedUrl) și golim starea temporară.
Cum gestionați voi buffering-ul de deep links când starea aplicației (auth, reîmprospătarea token-urilor) nu este încă complet pregătită la pornire?