How to support Universal Links in iOS App and setup server for it?
Asked Answered
F

3

40

How i can support Universal Links in my iOS application and setup my server in order to support Universal Links?

Faires answered 24/2, 2016 at 18:3 Comment(0)
F
102

Support Universal Links

When you support universal links, iOS 9 users can tap a link to your website and get seamlessly redirected to your installed app without going through Safari. If your app isn’t installed, tapping a link to your website opens your website in Safari.

Here, how to setup your own server, and handle the corresponding links in your app.


Setup Server

You need to having a server running online. To securely associate your iOS app with a server, Apple requires that you make available a configuration file, called apple-app-site-association. This is a JSON file which describes the domain and supported routes.

The apple-app-site-association file needs to be accessible via HTTPS, without any redirects, at https://{domain}/apple-app-site-association.

The file looks like this:

{
"applinks": {
    "apps": [ ],
    "details": [
        {
            "appID": "{app_prefix}.{app_identifier}",
            "paths": [ "/path/to/content", "/path/to/other/*", "NOT /path/to/exclude" ]
        },
        {
            "appID": "TeamID.BundleID2",
            "paths": [ "*" ]
        }
    ]
}
}

NOTE - Don’t append .json to the apple-app-site-association filename.

The keys are as follows:
apps: Should have an empty array as its value, and it must be present. This is how Apple wants it.
details: Is an array of dictionaries, one for each iOS app supported by the website. Each dictionary contains information about the app, the team and bundle IDs.

There are 3 ways to define paths:
Static: The entire supported path is hardcoded to identify a specific link, e.g. /static/terms
Wildcards: A * can be used to match dynamic paths, e.g. /books/* can matches the path to any author’s page. ? inside specific path components, e.g. books/1? can be used to match any books whose ID starts with 1.
Exclusions: Prepending a path with NOT excludes that path from being matched.

The order in which the paths are mentioned in the array is important. Earlier indices have higher priority. Once a path matches, the evaluation stops, and other paths ignored. Each path is case-sensitive.

Supporting Multiple Domains

Each domain supported in the app needs to make available its own apple-app-site-association file. If the content served by each domain is different, then the contents of the file will also change to support the respective paths. Otherwise, the same file can be used, but it needs to be accessible at every supported domain.

Signing the App-Site-Association File

Note: You can skip this part if your server uses HTTPS to serve content and jump to Application Setup guide.

If your app targets iOS 9 and your server uses HTTPS to serve content, you don’t need to sign the file. If not (e.g. when supporting Handoff on iOS 8), it has to be signed using a SSL certificate from a recognized certificate authority.

Note: This is not the certificate provided by Apple to submit your app to the App Store. It should be provided by a third-party, and it’s recommended to use the same certificate you use for your HTTPS server (although it’s not required).

To sign the file, first create and save a simple .txt version of it. Next, in the terminal, run the following command:

cat <unsigned_file>.txt | openssl smime -sign -inkey example.com.key -signer example.com.pem -certfile intermediate.pem -noattr -nodetach -outform DER > apple-app-site-association

This will output the signed file in the current directory. The example.com.key, example.com.pem, and intermediate.pem are the files that would made available to you by your Certifying Authority.

Note: If the file is unsigned, it should have a Content-Type of application/json. Otherwise, it should be application/pkcs7-mime.

Validate your Server with Apple App search validation tool
Test your webpage for iOS 9 Search APIs. Enter a URL and Applebot will crawl your webpage and show how you can optimize for best results https://search.developer.apple.com/appsearch-validation-tool/

Website Code

The website code can be found gh-pages branch on https://github.com/vineetchoudhary/iOS-Universal-Links/tree/gh-pages


Setup iOS Application

Application will target iOS 9 and will be using Xcode 7.2 with Objective-C.

Enabling Universal Links

The setup on the app side requires two things:
1. Configuring the app’s entitlement, and enabling universal links.
2. Handling Incoming Links in your AppDelegate.

1. Configuring the app’s entitlement, and enabling universal links.
The first step in configuring your app’s entitlements is to enable it for your App ID. Do this in the Apple Developer Member Center. Click on Certificates, Identifiers & Profiles and then Identifiers. Select your App ID (create it first if required), click Edit and enable the Associated Domains entitlement.

Next, get the App ID prefix and suffix by clicking on the respective App ID.

The App ID prefix and suffix should match the one in the apple-app-site-association file.

Next in Xcode, select your App’s target, click Capabilities and toggle Associated Domains to On. Add an entry for each domain that your app supports, prefixed with applinks:.

For example: applinks:vineetchoudhary.github.io

Which looks like this for the sample app:

Note: Ensure you have selected the same team and entered the same Bundle ID as the registered App ID on the Member Center. Also ensure that the entitlements file is included by Xcode by selecting the file and in the File Inspector, ensure that your target is checked.

Handling Incoming Links in your AppDelegate

[UIApplicationDelegate application: continueUserActivity: restorationHandler:] method in AppDelegate.m handles incoming links. You parse this URL to determine the right action in the app.

For example, in the sample app:

Objective-C

-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
    if ([userActivity.activityType isEqualToString: NSUserActivityTypeBrowsingWeb]) {
        NSURL *url = userActivity.webpageURL;
        UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UINavigationController *navigationController = (UINavigationController *)_window.rootViewController;
        if ([url.pathComponents containsObject:@"home"]) {
            [navigationController pushViewController:[storyBoard instantiateViewControllerWithIdentifier:@"HomeScreenId"] animated:YES];
        }else if ([url.pathComponents containsObject:@"about"]){
            [navigationController pushViewController:[storyBoard instantiateViewControllerWithIdentifier:@"AboutScreenId"] animated:YES];
        }
    }
    return YES;
}  

Swift :

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
      if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
          let url = userActivity.webpageURL!
          //handle url
      }
      return true
  }

iOS Application Code

The app code can be found master branch on https://github.com/vineetchoudhary/iOS-Universal-Links/

Note:

  1. Generally, any supported link clicked in Safari, or in instances of UIWebView/WKWebView should open the app.
  2. For iOS 9.2 and less, this will only work on a device. iOS 9.3 (still in beta at the time of writing) also supports the simulator.
  3. iOS remembers the user’s choice when opening Universal Links. If they tap the top-right breadcrumb to open the link in Safari, all further clicks will take them to Safari, and not the app. They can switch back to opening the app by default by choosing Open in the app banner on the website.

Done. It's all about Universal Links.


References

  1. Support Universal Links in your iOS App
Faires answered 24/2, 2016 at 18:3 Comment(25)
Will try that tomorrow. uploading a signed file to the staging.domain.com on HTTP it should work, right?Came
Great tutorial @VineetChoudhary, I have a question. What would happen if I open an url from Whatsapp for example? Does the Universal Link should work? Thanks!Physicochemical
@Physicochemical Yes.. because if any application tried to open an external url, it must call [[UIApplication sharedApplication] openURL:] and I think that's the selector where Universal Links stuff implemented by AppleFaires
Thanks @VineetChoudhary. I implemented Universal Links in my App, but when I try open an URL from Whatsapp for example it doesn't work. But when I open the url from Safari it works! I'm confused :/ If you have any suggestion, I appreciate it! Thanks again.Physicochemical
@Physicochemical As per Apple Universal Links documentation -> Universal links let iOS9 users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps. There they have mentioned Call to openURLFaires
Can you add your apple-app-site-association file? @VineetChoudharyDurwyn
@UdanPirappu you can find it here github.com/vineetchoudhary/iOS-Universal-Links/blob/gh-pages/…Faires
Hi, I have tried this but unfortunately ,suppose my app is not install on IOS 9 device that time I just browsed the universal link in safari but it is not redirecting to app store it is downloading file ,actually it should redirect.How to solve this?where I am wrong?Lenna
Thank you for this amazing tutorial.Plurality
Thanks @VineetChoudhary For this great answer. I still have a question, I have a site accessible via HTTPS, with self signed certificates, with the app-site-association-file on the root folder. But still not working. So I was thinking about the self signed certificate that can be causing the problem. what do you think ?Principalities
@Principalities I never tried universal links with self signed certificate. You can try with github pages pages.github.com or firebase hosting firebase.google.com/docs/hostingFaires
@VineetChoudhary Thanks a lot. But you are telling me that we can host the association file on a server which is different from the one that will be on links that will redirect the user to the app ??Principalities
@Principalities just for testing purposesFaires
Amazing tutorial man. Thanks. I was missing the applinks: keyword before the domain name.Grantham
Swift 3 AppDelegate method signature - func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> BoolCavallaro
If I am using iOS 10 and Xcode 8.3.2 then it will work ?Create
@kishor0011 yes, It'll work but try in physical device with distribution provisioning profile.Faires
@VineetChoudhary I have iPhone 5s and distribution profile also. But I am not getting actually. #46056347Create
My target is not set for entitlements file and also unable to set it. clicking on it does nothing. Any help would be appreciated.Spencerspencerian
@VineetChoudhary Is it work on development build or have to upload on beta test ?Autotomize
How we can make universal link work with development profile? I am installing app via debug mode from xcode and universal link doesnt work, but works fine if I install app from app store, any help?Trihydric
Can somebody help me out with similar issue: #65489955Shakti
@VineetChoudhary Not working when i hit url from the browser and it work when i click url from messenger or whatsapp.Deckle
@VineetChoudhary I saw that you give AASA file that can support multiple app in a single AASA, I’m trying to do the same and also matched the format as you write, but got no luck. The apps that launch is always app A (given I want to support opening link to app A and app B) even I click the url that suppose to be open in app B. can you give me some advice?Fitter
what about Facebook ? Seems like I followed your directions and Facebook is not opening the application.Doley
M
4

Here is the code for handling universal links in Swift 3+, based on the answer by Vineet Choudhary:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let url = userActivity.webpageURL {
        //handle URL
    }

    return true
}
Merrill answered 19/9, 2018 at 10:55 Comment(1)
In my case not working when i hit url from the browser and it work when i click url from messenger or whatsapp.Deckle
M
2

If you complete all of Vineet's post and it still doesn't work, try to use a distribution provisioning profile. I did everything from the post above, but had to issue a distribution profile in order to make it work.

With development profile the app successfully downloaded the AASA file, but it never open my application when I was pressing the links.

Hope it helps.

Militarize answered 8/6, 2017 at 8:41 Comment(4)
Could be another useful hint. Do you employ manual signing, rather than checking the box at "Automatically manage signing" in the General Tab and let Xcode do the signing setup for you?Janot
Manually. We actually found that the provisioning profile was incorrectly generated. We added the TeamID to the provisioning profile ID, which resulted in a double prefix. Removing that actually fixed the error, no matter develop or distribution profile. You could try by generating a separate profile with an unique ID just for testing. After that make sure the bundle ID in Xcode is the same as the one in the profile and manually sign that.Militarize
@Militarize Is it work on development build or have to upload on beta test ?Offertory
Thanks. If this works: "paths": [""], but variations with NOT doesn't work "paths": ["NOT /forgotPassword", ""] or "paths": ["*", "NOT /forgotPassword"] then this distribution/development profile is not the issue, right? Then the issue is something else, right?Shakti

© 2022 - 2024 — McMap. All rights reserved.