Skip to content

Instantly share code, notes, and snippets.

@anhar
Last active April 9, 2024 05:34
Show Gist options
  • Save anhar/6d50c023f442fb2437e1 to your computer and use it in GitHub Desktop.
Save anhar/6d50c023f442fb2437e1 to your computer and use it in GitHub Desktop.
Setting up iOS Universal Links
@alvynfash
Copy link

alvynfash commented Dec 12, 2020

I've been trying to get this to work on a local web server. I have a valid self signed CA/cert installed on the simulator and my computer, and the server is using it too. The cert complies to the ios13 requirements. It shows up as trusted in safari on my macbook and on the simulator.

The domain I'm using is setup with a local network dns server so it points to my dev server. I put this in my entitlements file like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.associated-domains</key>
	<array>
		<string>applinks:myserver.test</string>
	</array>
</dict>
</plist>

My app identifier and provisioning profile are both configured with the capability for universal links enabled.

I created an apple-app-site-association file like this:

{
  "applinks": {
    "details": [
      {
        "appIDs": [
          "MY APP/BUNDLE ID IS HERE"
        ],
        "components": [
          {
            "/": "/user/*",
            "comment": "Match user profile from URL"
          }
        ],
        "paths": [
          "/user/*"
        ]
      }
    ]
  }
}

When I visit my local server, I get the json response. Its content type is application/json and it's a 200 status code. I put the same file so it's at the root and also inside the .well-known folder.

When I install/run my app on the simulator, the web server never receives the request. I've only been able to find these messages in the console logs:

default	14:31:07.186484-0400	nsurlsessiond	Task <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45> dataTaskWithRequest: <NSMutableURLRequest: 0x7fa29401d6c0> { URL: https://myserver.test/apple-app-site-association } [allowsCellularAccess: 1]
default	14:31:07.187330-0400	nsurlsessiond	Task <196F8D8F-971F-447B-AFB1-9955A063094F>.<44> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/.well-known/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/.well-known/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>, NSLocalizedDescription=cancelled}
default	14:31:07.187716-0400	nsurlsessiond	Task <196F8D8F-971F-447B-AFB1-9955A063094F>.<44> completed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/.well-known/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/.well-known/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>, NSLocalizedDescription=cancelled} [-999]. C(U)
default	14:31:07.189024-0400	swcd	Task <196F8D8F-971F-447B-AFB1-9955A063094F>.<44> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/.well-known/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/.well-known/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>",
    "LocalDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <196F8D8F-971F-447B-AFB1-9955A063094F>.<44>, NSLocalizedDescription=cancelled}
default	14:31:07.220611-0400	nsurlsessiond	Task <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>, NSLocalizedDescription=cancelled}
default	14:31:07.220698-0400	nsurlsessiond	Task <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45> completed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>, NSLocalizedDescription=cancelled} [-999]. C(U)
default	14:31:07.221162-0400	swcd	Task <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://myserver.test/apple-app-site-association, NSErrorFailingURLKey=https://myserver.test/apple-app-site-association, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>",
    "LocalDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <87162B3D-0D26-4D62-9E46-0577CB1B8BA4>.<45>, NSLocalizedDescription=cancelled}

It's clear the request never hits the web server because I don't see it in the log like when I go to the url directly in my browser. Anyone have any idea how I can make this work in a local dev server for testing?

@acoutts Depending on which OS you're testing on, i resolved this by following the changes by apple here (take a look at developer mode)

Also, there's an option for this that can be found in Settings>Developer>Associated Domains.
Try toggling it to on

@ozgurshn
Copy link

ozgurshn commented Jul 12, 2021

Should App be uploaded to TestFlight to test the association? I'm working on a new app, created the app on AppStore to get appId but didn't upload any build yet. I can't make it work with Xcode builds deployed to my iPhone.
This setting is checked Settings>Developer>Associated Domains and developer mode is used.

File validation gives this result:
Link to Application |
Action required
Could not extract required information for application links. Learn how to implement the recommended Universal Links.
| Error cannot fetch app site association

@gerardnll
Copy link

gerardnll commented Aug 27, 2021

Bad mime-type in gist, it's 'application/json', application word is repeated:

location /apple-app-site-association {
    default_type application/application/json;
}

@MikeZnnam
Copy link

MikeZnnam commented Jan 8, 2022

Dear colleagues,

I'm struggling now for more than one week at this topic and still it is not working.
I get the following error while apple validation:

Link to Application |
Action requiredCould not extract required information for Universal Links. Learn how to implement the recommended Universal Links. |
Error no apps with domain entitlementsThe entitlement data used to verify deep link dual authentication is from the current released version of your app. This data may take 48 hours to update.

I created apple-app-site-association. See the following link: https://pflanzenkreisel.de/apple-app-site-association/

Within Xcode I configured the following:

  1. URL Types: Identifier:pflanzenkreisel URL Schemas: pflanzenkreisel
  2. applinks:pflanzenkreisel.de was added in associated domains
  3. appDelegate following was added:
#import <React/RCTLinkingManager.h>

- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}

Can you please help me to solve this issue?

Thank you so much!
Mike

@no-toodate
Copy link

thank you !

I am so sorry, I normally don't use MacOS so I am not familiar with objective-C or swift. I really don't know what to do in the 'handle url' parts in the AppDelegate file. What exactly do I need to do? Everytime I am looking at tutorials, I only have "handle this" and I really don't know what to do!

@AlexMoumou
Copy link

AlexMoumou commented Feb 23, 2023

@anhar Hello, this guide was a saviour.

Ive managed to make it work on Safari where a banner pops up on the paths i have specified. It also works through mail apps even if i have a different default browser than Safari. It does not work for Chrome, Firefox or any other browser though.

Is this even possible in iOS or do we have to use a custom scheme which will be called by our site?

Please let me know if anyone else has faced this issue.

Edit: After some testing i found out that it works with default browser Chrome/Firefox if you click a link you support from outside your domain. The following link was very useful in terms of what works from where and when: https://help.branch.io/developers-hub/docs/ios-universal-links

@elomonaco
Copy link

@anhar Hello, this guide was a saviour.

Ive managed to make it work on Safari where a banner pops up on the paths i have specified. It also works through mail apps even if i have a different default browser than Safari. It does not work for Chrome, Firefox or any other browser though.

Is this even possible in iOS or do we have to use a custom scheme which will be called by our site?

Please let me know if anyone else has faced this issue.

Apple assumes you're using Safari, so it won't work with any other browser because Apple thinks of itself and no one else. Honestly whether the app says Chrome, or Firefox or something else, it's really safari under the hood, because Apple strictly prevents another browser engine on the iOS ecosystem, so really your hand is forced to use safari if you want the best experience.

@matthewgallagherskybet
Copy link

@anhar amazing article, covers a lot more caveats than I've seen anywhere else I've looked.

There is one thing I'm a little uncertain on, is it possible to exclude a subdomain using components, or is this done by just not including that subdomain as an applinks option within the apps Associated Domains capability?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment