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
@rupeshpatil761
Copy link

rupeshpatil761 commented Apr 13, 2017

Hello @anhar , I went through above document and did changes as mentioned. Even though I am able to validate my site. Please help me. I am using ngnix server. Thanks in Advance

@eXhausted
Copy link

too good to be true

@jmarus
Copy link

jmarus commented Apr 24, 2017

Does the file have to be signed, even if it's being forced over https?

@vj-repo
Copy link

vj-repo commented May 20, 2017

Hi, Have you tried opening universal links with in the associated domains? I mean if you have only part of website is available for Mobile then you need to remove this "paths": [ "*" ].

So, when you are in the home page of "example.com" on safari app and if you navigate to "https://example.com/articles/some", it should open app. But for some reasons, it is navigating with in the safari.

It works from outside of associated domains but not from with in the associated domains. Is this how it should be or something wrong with my code?

Thanks in advance,
VJ

@dqlobo
Copy link

dqlobo commented Jun 6, 2017

@vj-repo: I think it's because of this clause in the documentation:

"When a user is browsing your website in Safari and they tap a universal link to a URL in the same domain as the current webpage, iOS respects the user’s most likely intent and opens the link in Safari. If the user taps a universal link to a URL in a different domain, iOS opens the link in your app."

@anhar
Copy link
Author

anhar commented Jul 18, 2017

@jmarcus Yes the the apple-app-site-association must be signed with the webservers certificates.

@vj-repo Yes, that was an intentional way of showing how to use the wildcard character for all paths.
If you only want to support certain paths of your website you should remove the wildcard part from the example JSON.

@eusonlito
Copy link

@jmarcus, depending the client iOS version https://developer.apple.com/documentation/security/shared_web_credentials/preparing_your_app_and_website_to_share

The apple-app-site-association file must meet the following requirements:

  • The file must be hosted on an https:// site with a valid certificate (for example, Safari must not issue a certificate warning when viewing the site).
  • The file must not use any redirects.
  • In iOS 9.3.1 and later, the file must be no larger than 128 KB (uncompressed), regardless of whether it is signed.
  • If your app runs in iOS 9 and later and you use HTTPS to serve the file, you can create a plain text file that uses the application/json MIME type and you don’t need to sign it.
  • If your app runs in iOS 8, the file must have the MIME type application/pkcs7-mime and it must be CMS signed by a valid TLS certificate.

File validator on server: https://branch.io/resources/aasa-validator

@cuongnv-ibl
Copy link

How can we show popup Open App when user copy and then paste it that universal link on Safari iOS address bar?

@raresloth
Copy link

@prcongithub
Copy link

prcongithub commented Oct 15, 2018

I face this issue even after following the exact same steps.

Your file must be served with content type "application/pkcs7-mime"

I am using nginx

location /apple-app-site-association {
    root /data;
    access_log  /mnt/log/nginx/apple-file.access.log;
    access_log  /mnt/log/nginx/apple-file.error.log;
    default_type application/pkcs7-mime;
  }

I receive no requests on my server when I validate this using the validator tool

@aaronbalthaser
Copy link

aaronbalthaser commented Dec 24, 2018

@prcongithub

I face this issue even after following the exact same steps.

Your file must be served with content type "application/pkcs7-mime"

I am using nginx

location /apple-app-site-association {
    root /data;
    access_log  /mnt/log/nginx/apple-file.access.log;
    access_log  /mnt/log/nginx/apple-file.error.log;
    default_type application/pkcs7-mime;
  }

I receive no requests on my server when I validate this using the validator tool

Did you ever find a solution? I'm getting the same response from the test tool. I am using Godaddy hosting.

@newsplash24
Copy link

Thank you very much, i was struggling and this article helped me a lot, especially the server configuration part. Thank you again, god bless you :)

@angelorobo
Copy link

Docs say to use application/json as the MIME type: https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html

Yes this is what I saw too.

@anhar
Copy link
Author

anhar commented Feb 3, 2020

@newsplash24: Thank you for the kind words 😊

As for the application/pkcs7-mime vs application/json debacle I've updated the docs to make it a little clearer what you need depending on your minimums OS. Hope that clears it up.

@jowinst
Copy link

jowinst commented Apr 29, 2020

Hi @anhar! I'd like to ask you if you have an example of an AASA file compatible both for iOS 12 and iOS 13. I am not completely sure which format should the JSON have to support both OS.

@anhar
Copy link
Author

anhar commented May 5, 2020

Hi @jowinst! If you wanna support both iOS 12 and iOS 13 use the older format. Once you have deprecated iOS 12 in the future you can migrate to using the newer more customizable format.

If you need to use both I guess you could take a look at the User-Agent header and then return different json files depending on the OS version but honestly I think that's just unnecessarily complicated.

@Mukul13cs002
Copy link

applinks:*collpoll.com is defined.

A wildcard by prefixing * is not working in my case.
Please help me

@acoutts
Copy link

acoutts commented May 16, 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?

@Hanzofm
Copy link

Hanzofm commented Jun 17, 2020

Hi to all, thanks for your guide.

I am trying to validate my file but I am obtaining:

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

This is my file:

{
  "applinks": {
      "details": [
           {
             "appIDs": [ "1111111111.com.mybundleid.app" ]
           }
       ]
   },
   "webcredentials": {
      "apps": [ "1111111111.com.mybundleid.app" ]
   }
}

What would be doing wrong?

@tevla
Copy link

tevla commented Aug 27, 2020

<Directory /path/to/root/directory/>
...
</Directory>
<Files apple-app-site-association>
      Header set Content-type "application/json"
</Files>

In Apache2, for me adding the Files directive outside of the Directory work, any idea why?

@showchi
Copy link

showchi commented Nov 30, 2020

In fact, the appIDs are not always Team id. Some apps that were created earlier are a random code called Application id prefix;
You can go to the Apple developer website to verify.

@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