Who knew deep links could be so complicated? Fortunately hosting the server-side files for iOS Universal Links and Android App Links is the easy part with Phoenix.

Create manifests

cd $my_app_root
mkdir priv/static/well-known
cd priv/static/well-known

In this directory, create your iOS manifest apple-app-site-association:

{
  "applinks": {
    "apps": [],
    "details": [
    {
      "appID": "N9827423.io.my_ios_app.app",
      "paths": ["/r/*"]
    },
    {
      "appID": "N9827423.io.my_ios_app.app-dev",
      "paths": ["/r/*"]
    }
    ]
  }
}

Then create the Android App Links manifest assetlinks.json:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

By default, Phoenix ignores the priv/static directory from version control, so we need to explicitly add it by running git add -f priv/static/well-known. If you prefer, you could use a different directory altogether to avoid using the -f flag.

Serve manifests with Plug.Static

# lib/my_app_web/router.ex

defmodule MyAppWeb do

  # Below existing Plug.Static, at: "/"
  plug Plug.Static,
    at: "/.well-known",
    from: {:my_app, "priv/static/well-known"},
    gzip: false,
    content_types: %{"apple-app-site-association" => "application/json"}
    
  # ...
end

Above we have added a second Plug.Static call to serve from /.well-known base path. We named the directory .well-known to make sure the directory was not hidden when browsing the project source.

Lastly we set the Content-Type to JSON for the iOS file, otherwise it would be served as text/plain.

Verify everything works

You can validate you configured everything correctly using App Links tester and Universal Links tester.

Reader Question

Would you find this useful to be packaged as a hex package that generates these files and serves them? Send me a message on Twitter and let me know!