iosreactjsfirebaseios-universal-linksapplinks

How to setup Associated Domains / Universal Links


I am trying to redirect the user to the installed app when visiting www.example.com/success as well as show a banner when visiting the homepage www.example.com.

With my current implantation the url DOES NOT redirect and open the app and neither does the website display a banner.

I am following the documentation found here https://developer.apple.com/documentation/safariservices/supporting_associated_domains

My website is a simple create-react-app with a homepage hosted with Firebase.

I have done the following:

  1. Add Associated Domains to Signing and Capabilities - applinks:website.com/success
  2. Add apple-app-site-association file to /public/
  3. Amend firebase.json file
  4. Link to files in index.html

What I am using to validate:

https://branch.io/resources/aasa-validator/

https://search.developer.apple.com/appsearch-validation-tool

Testing by adding www.example.com/success link to notes and opening, It always opens in safari. I Have also tried reinstalling the app and restarting the phone.

Apple API Validation

I have uploaded a new app version with associated domains to the store

enter image description here

Branch.io

{
    "applinks": {
        "apps": [],
        "details": []
    }
}

When visiting www.example.com/apple-app-site-association

{ "activitycontinuation": { "apps": [ "team.com.example.com" ] }, "applinks": { "apps": [], "details": [ { "appID": "team.com.example.com", "paths": [ "/", "/", "/success", "/success/",] }, "webcredentials": { "apps": [ "team.com.example.com" ] } }

apple-app-site-association

{
  "activitycontinuation": {
      "apps": [
        "team.com.example.com"
      ]
  },
  "applinks": {
      "apps": [],
      "details": [
            {
                "appID": "team.com.example.com",
                "paths": [
                  "/",
                  "/*", 
                  "/success",
                  "/success/*", 
                ]
            },
      ]
  },
  "webcredentials": {
      "apps": [
        "team.com.example.com"
      ]
  }
}

Index.html

<link rel="apple-app-site-association file" href="%PUBLIC_URL%/apple-app-site-association">
<link rel="apple-app-site-association file" href="/apple-app-site-association">
<meta name="App" content="app-id=XXX, app-argument=https://apps.apple.com/US/app/APP/idXXX, affiliate- data=optionalAffiliateData">

Firebase.json

{
  "hosting": {
    "public": "public",
    "headers": [
      {
        "source": "/apple-app-site-association",
        "headers": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ]
      }
    ],
    "appAssociation": "NONE"
  }
}

Device Console

When installing the app I monitor the device console in Xcode looking through swdc process logs for anything related to associated domains or requests. Here's some I found;

Error getting enterprise-managed associated domains data. If this device is not enterprise-managed, this is normal: Error Domain=SWCErrorDomain Code=1701 "Failed to get associated domain data from ManagedConfiguration framework." UserInfo={NSDebugDescription=Failed to get associated domain data from ManagedConfiguration framework., Line=298, Function=}

Entry { s = applinks, a = , d = au….ub….com, ua = unspecified, sa = approved } needs its JSON updated because the app PI changed

https://developer.apple.com/library/archive/qa/qa1916/_index.html


Solution

  • First at your device logs and your JSON format it seems your should be following the example shown here: https://developer.apple.com/documentation/safariservices/supporting_associated_domains

    In your logs it may shows logs from other apps who also have the outdated API format. However this should not be so much of an issue.

    {
      "applinks": {
          "details": [
               {
                 "appIDs": [ "ABCDE12345.com.example.app", "ABCDE12345.com.example.app2" ],
                 "components": [
                   {
                      "#": "no_universal_links",
                      "exclude": true,
                      "comment": "Matches any URL whose fragment equals no_universal_links and instructs the system not to open it as a universal link"
                   },
                   {
                      "/": "/buy/*",
                      "comment": "Matches any URL whose path starts with /buy/"
                   },
                   {
                      "/": "/help/website/*",
                      "exclude": true,
                      "comment": "Matches any URL whose path starts with /help/website/ and instructs the system not to open it as a universal link"
                   },
                   {
                      "/": "/help/*",
                      "?": { "articleNumber": "????" },
                      "comment": "Matches any URL whose path starts with /help/ and which has a query item with name 'articleNumber' and a value of exactly 4 characters"
                   }
                 ]
               }
           ]
       },
       "webcredentials": {
          "apps": [ "ABCDE12345.com.example.app" ]
       },
        "appclips": {
            "apps": ["ABCED12345.com.example.MyApp.Clip"]
        }
    }
    

    Secondly following the steps in Apple's troubleshooting and your problem I am guessing your problem might be Step 7

    You have not posted how you handle the URL. You need to implement the UIUserActivityRestoring method as suggested in the other answer for it to work. Here is a SwiftUI version

    ContentView()   
        .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
            
            print("Continue activity \(userActivity)")
            guard let url = userActivity.webpageURL else {
                return
            }
            
            print("User wants to open URL: \(url)")
            // TODO same handling as done in onOpenURL()
    
        }
    

    Also add

    .onOpenURL { url in
        print("URL OPENED")
        print(url) // parse the url to get someAction to determine what the app needs do
        if url.relativeString == "example://success" {
    
        }
    }
    

    https://developer.apple.com/library/archive/qa/qa1916/_index.html