Universal Links trên iOS và App Links trên Android đều yêu cầu một file JSON được phục vụ qua HTTPS ở gốc tên miền của bạn. iOS muốn /.well-known/apple-app-site-association. Android muốn /.well-known/assetlinks.json. localhost không thể phục vụ cái nào qua một hostname mà OS sẽ liên kết với app của bạn. Vậy nên ta tunnel.
Yêu cầu thực tế trên mỗi nền tảng
iOS Universal Links
OS lấy https://your-domain.com/.well-known/apple-app-site-association (hoặc /apple-app-site-association) khi app của bạn được cài. Nó đối chiếu nội dung với entitlement associated domains của app. Việc lấy phải thành công qua HTTPS thật. iOS không tin chứng chỉ tự ký cho mục đích này và không tin localhost.
Android App Links
Android xác minh App Links bằng cách lấy https://your-domain.com/.well-known/assetlinks.json. File phải liệt kê các fingerprint SHA-256 của app. Việc xác minh diễn ra lúc cài và lần khởi chạy đầu tiên. Cùng ràng buộc: HTTPS thật, tên miền thật.
Vì sao localhost không làm trực tiếp được
Ngay cả khi mkcert cho bạn HTTPS đáng tin trong trình duyệt laptop, OS di động không tin CA cục bộ của bạn. Và tên miền trong apple-app-site-association phải khớp với associated-domain trong entitlement của app — đó là một tên miền thật phân giải được qua DNS, không phải localhost.
Một số nhóm né điều này bằng một tên miền staging riêng. Cách đó chạy được, nhưng vòng lặp lặp lại chậm. Một tunnel localhost cho bạn một URL HTTPS thật trên một subdomain thật mà thiết bị di động tiếp cận được không phàn nàn.
Cách đấu nối
- Phục vụ
apple-app-site-associationvàassetlinks.jsontừ máy chủ dev cục bộ tại/.well-known/. Cả hai file. Cả hai route. - Chạy
npx portpreview 3000(hoặc cổng máy chủ dev của bạn dùng). - Ghi lại hostname tunnel — kiểu
abc123.portpreview.dev. - Trong entitlement associated domains của app iOS, thêm
applinks:abc123.portpreview.dev. Trong intent filter của Android, thêm cùng host đó. - Build và cài app lên thiết bị thật (giả lập có hành vi Universal Link kỳ lạ).
- Mở URL từ một app khác — Notes, Mail, một mã QR — và xem nó định tuyến vào app đã cài thay vì trình duyệt.
Điểm vướng khi tunnel cho deep link: hostname tunnel thay đổi giữa các phiên trừ khi bạn có subdomain dành riêng. Mỗi lần xoay vòng nghĩa là build lại app với mục associated-domain mới. Nếu bạn lặp hành vi deep link thường xuyên, hãy lấy một subdomain dành riêng.
content-type quan trọng với apple-app-site-association
iOS mong file không có đuôi và content-type application/json (iOS mới hơn) hoặc application/pkcs7-mime (định dạng cũ, đã ký). Gần như mọi app hiện đại dùng biến thể JSON thuần. Đảm bảo máy chủ dev của bạn trả về đúng content-type, nếu không iOS lặng lẽ từ chối file mà không có lỗi hữu ích.
Kiểm thử từ trình duyệt desktop trước: mở https://your-tunnel.portpreview.dev/.well-known/apple-app-site-association và xác nhận JSON hiển thị và header content-type trong dev tools đúng. Nếu sai, sửa trước khi truy lỗi Universal Link trong giả lập.
Các bẫy gỡ lỗi deep link khác
Lệch entitlement của app
Nếu entitlement associated-domains của bạn ghi applinks:abc.portpreview.dev nhưng apple-app-site-association liệt kê def.portpreview.dev, iOS không lấy. Hostname phải nhất quán.
Universal Link từ bên trong Safari
Chạm một Universal Link bên trong Safari (cùng app mà tab đang mở) đôi khi mở trong Safari thay vì app. Đây là cố ý — iOS ngăn chiêu "mở app mỗi khi người dùng nhấp một liên kết". Hãy kiểm thử từ Notes hoặc Mail.
Android App Links và digital asset links
Android cũng hỗ trợ scheme URL tùy chỉnh (yourapp://path), vốn không cần HTTPS hay assetlinks.json. Chúng dễ kiểm thử hơn nhưng kém an toàn hơn — bất kỳ app nào cũng đăng ký được cùng scheme. Để deep linking chất lượng sản xuất, App Links là câu trả lời.
Luồng kiểm thử di động chúng tôi dùng
Cho một dự án xuất bản cả iOS và Android với deep link:
- Khởi động backend phục vụ các file
.well-known. - Tunnel nó với
npx portpreview 3000. - Khi URL tunnel ổn định cho phiên (hoặc dùng subdomain dành riêng), cập nhật các mục associated-domain của app và build.
- QA trên thiết bị vật lý — cả cài mới lẫn cập nhật.
- Với combo OAuth-và-deep-link (nhà cung cấp đăng nhập chuyển hướng về app), ghép nó với kiểm thử callback OAuth.
Thiết lập gây phiền ở lần đầu. Sau đó, URL tunnel chỉ là một biến môi trường khác trong scheme Xcode và Gradle.
Để có các mẫu kiểm thử di động rộng hơn, xem kiểm thử di động với tunnel localhost. Tham gia danh sách chờ của PortPreview.