Recently, I worked on adding Apple’s Universal Links support in our project together with a colleague. In case you don’t know what universal links are, it’s the ability of the apps and web pages cooperating to register certain web URLs to open with the app instead of the browser. This feature became available in iOS version 9, and seems to have required two “minor” releases to stabilize.
Aside from the Apple Developer’s documentation, you can find how-tos in the internet for setting up universal links support for your app. I found the howtos in this blog post, this blog post, and this stackoverflow question useful.
Universal Links definitely have their own quirks. This page from branch.io nicely summarizes universal links behavior in different apps and points out some unexpected behavior. This stack overflow question is also worth taking a look.
An interesting behavior of the universal links implementation is that the browser does not launch your app if the link that you configured to launch your app is clicked on a page whose address has the same domain as your link. Instead, the web page is displayed, and the user has to drag the page down to reveal a banner that include a link for opening the app. This is problematic for two reasons. One: it’s unlikely that any user would pull down that page to reveal a banner because iOS provides no visual ques as to that banner’s existence. And two: most of us who develop web applications that are integrated with mobile apps, want to be able to link to the page that launch the mobile app within our web application. For example, if I am developing a rails app that runs under domain app.mydomain.com, and I configured my app to launch for the URL app.mydomain.com/latest, I want to be able to put the link to app.mydomain.com/latest on my root page app.mydomain.com/.
Our solution was to set up a minimal reverse proxy on a different domain, which redirects he browser to the original page, and modifying all links that would launch the app to point to the reverse proxy address instead of the page in our web application.
Continuing the last example, here is a virtual host definition for nginx for defining this reverse proxy:
server { server_name ul-fix-app.mydomain.com; listen 443 ssl; ssl_certificate .crt; ssl_certificate_key .key; location = /.well-known/apple-app-site-association { default_type application/json; root ; } location = /apple-app-site-association { default_type application/json; root ; } location = /latest { # Redirect to the main server, in case application is not installed rewrite ^ https://app.mydomain.com$request_uri? permanent; } }
Then, the apple-app-site-association file contains this:
{ "applinks": { "apps": [], "details": [ { "appID": ".", "paths": ["/latest"] } ] } }
That’s it. Once we update the links in our web application to point to ul-fix-app.mydomain.com/latest instead of app.mydomain.com/latest, it will always launch the app, regardless of whether the link was clicked in our web application or on some other page.
Beware that you can not configure a redirect for the apple-app-site-association files. iOS will not follow your redirection, thus it will not read your association file, and your universal links won’t work.
Note: You might want to use a different domain, e.g. launch.mydomain.com/latest, or a sub-domain, e.g. launch.app.mydomain.com/latest, depending on your devops context. Be aware that the sub-domain version may require the domain be added to your existing ssl certificate, or a new certificate for that sub-domain.
Setting up universal links require energy and patience sometimes. Good luck.