1.1 What is Universal Link

Universal Link is one of the new iOS9 features apple unveiled at WWDC. This feature is similar to deep linking and makes it easy to launch your client application directly by opening an Https link (the phone has an App installed). Compared to the URL Scheme used in the past, this new feature provides an excellent user experience when implementing seamless links in the Web-app.

When your APP supports Universal Link, when a user clicks on a Link they can jump to your website and get a seamless redirection to the corresponding APP, without going through Safari. If your app doesn’t support it, it opens the link in Safari. You can see it in the Apple developers:

Seamlessly link to content inside your app, or on your website in iOS 9 or later. With universal links, you can always give users the most integrated mobile experience, even when your app isn’t installed on their device.

1.2 Application Scenarios of Universal Link

The Universal Link allows users to pull up Safari or other apps from their Webview and use the corresponding functions in the APP to divert users to the APP.

What exactly is this scenario? For example, if your user visits your company’s web page in Safari and has your company’s App installed on their phone at the same time; Universal Link enables users to directly open your app when opening a detail page and reach the corresponding content page in the app, so as to implement the user’s desired operations (such as viewing a news, viewing the details of a product, etc.). For example, when you enter taobao’s web page in Safari and click to open the APP, Universal Link will be used to pull up taobao APP.

1.3 Benefits of Universal Link jumps

  • Unique: Unlike custom URL schemes, which use the standard HTTPS protocol to link to your Web site, they are generally not declared by other apps. In addition, because URL scheme is a custom protocol, it cannot be opened directly without app installation (there will be an unopenable popover in Safari), while Universal Link itself is an HTTPS Link, so it has better compatibility.

  • Security: When the user has your APP installed on their phone, the system will go to the site you configured and download the instructions file you uploaded (the instructions file states that the current HTTPS link can open the APP). Since only you can upload files to the root directory of your website, the association between your website and your APP is secure;

  • Variable: Universal Link works even when the user does not have your APP installed on their phone. If you like, when you don’t have your app installed, the user clicks on the link and it shows you the content of your site in Safari;

  • Simple: an HTTPS link that works with both the website and the APP;

  • Private: Other apps can communicate with your APP without knowing whether your APP is installed or not.

2. Configure and run the Universal link

2.1 Configuring the App ID Support Associated Domains

Go to developer.apple.com/, find the App ID, and change it to Enabled with Associated Domains in the Application Services list.

! [](https://static001.geekbang.org/infoq/9a/9aeea1dfab91b136fbfb114ed4fb792f.png)

2.2 Configuring the iOS App Project

Xcode version 11.0

Targets ->Signing&Capabilites->Capability->Associated Domains = targets->Signing&Capabilites->Capability->Associated Domains = applinks:

The specific steps are shown below:

! [](https://static001.geekbang.org/infoq/6a/6ad0b18feb32811edc6c11f5f86e4680.png)
! [](https://static001.geekbang.org/infoq/f5/f5c2afa52c14eb521265af60e8373e77.png)
! [](https://static001.geekbang.org/infoq/41/41e65450d1623f28530881686de2d8ee.png)

Xcode version 11.0 or later

Targets ->Capabilites->Associated Domains = targets->Capabilites->Associated Domains = targets->Capabilites->Associated Domains = applinks: 563513413, no matter you are big ox or small white are welcome to enter

Associated Domains in the project:

! [](https://static001.geekbang.org/infoq/f4/f4abafb3154822c7ec3b2262e24d110c.png)

2.2 Configuring and Uploading apple-app-Association

Which urls are recognized as UniversalLink? See the Apple-app-association file Apple Document universallinks.html

  • Your domain name must support Https

  • Put the apple-app-association file in the domain root or.well-known directory, without any suffix

  • The file is JSON and can be saved as text

  • Json As required on the official website

Apple – the app – site – association template:

{    "applinks": {        "apps": [],        "details": [            {                "appID": "9JA89QQLNQ.com.apple.wwdc",                "paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]            },            {                "appID": "ABCD1234.com.apple.wwdc",                "paths": [ "*" ]            }        ]    }}
Copy the code

Description:

**appID: ** Formed by teamId. Yourapp’s Bundle Identifier. 9JA89QQLNQ, above, is teamId. Log in to the Developer Center and find the Team ID under Account -> Membership.

** Paths: ** Sets the list of paths supported by your app. Only links to these specified paths can be handled by your app. The asterisk is written to represent all links under a recognizable domain name.

Upload the specified file: Upload the file to the root directory of your domain name or. Well-known directory so that Apple can access your uploaded file. After uploading the file, visit it to see if you can get it. When you enter the file link in your browser, you should download the apple-app-site-association file directly.

2.4 How do I Verify that Universal Link Takes Effect

  • You can use the iOS memo program, enter the link, and hold down the link. If “Open in ‘XXX'” is displayed in the menu, the configuration takes effect.

  • Or if the url to be tested opens in Safari and slides over the page that appears, you can see it opened in the “XXX” app and the menu appears:

! [](https://static001.geekbang.org/infoq/eb/ebff709967c1261aa6e1ed80686885c8.png)

When you click a link, you can directly enter our app, but our goal is to get the link that the user entered and show the corresponding content to the user according to the link.

Implement the delegate method in the AppDelegate, official link: Handling Universal Links

Objective-C:

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler { if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { NSURL *url = userActivity.webpageURL; If (url is what we want to process) {// do our processing} else {[[UIApplication sharedApplication] openURL:url]; } } return YES; }Copy the code

Swift:

func application(_ application: UIApplication,                 continue userActivity: NSUserActivity,                 restorationHandler: @escaping ([Any]?) -> Void) -> Bool{    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,        let incomingURL = userActivity.webpageURL,        let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),        let path = components.path,        let params = components.queryItems else {            return false    }        print("path = \(path)")        if let albumName = params.first(where: { $0.name == "albumname" } )?.value,        let photoIndex = params.first(where: { $0.name == "index" })?.value {                print("album = \(albumName)")        print("photoIndex = \(photoIndex)")        return true            } else {        print("Either album name or photo index missing")        return false    }}
Copy the code

3. Problems encountered by Universal Link and solutions

3.1 cross domain

Front-end development often faces cross-domain problems, and Universal Link also has cross-domain problems, but the difference is that Universal Link, must require cross-domain, if not cross-domain, it does not work, it fails, it does not work. (This is how Apple designed iOS after 9.2.)

This is also a question highlighted when taking Zhihu as an example. Why does Zhihu use oia.zhihu.com to do Universal Link?

  • Suppose the domain name of the current page is A

  • The domain name for the current web page is B

  • Universal Link is triggered only when B and A are different domain names

  • If B and A are the same domain name, it will only continue to jump in the current WebView, even if your Universal Link is normal, you will not open the App at all

Is not quite easy to understand, that directly take Zhihu for example

The Universal Link of Zhihu is configured with the domain name oia.zhihu.com and identifies the urlpath under the domain name, such as /answers /questions /people. That is to say, Zhihu universal link, only when you visit https://oia.zhihu.com/questions/xxxx, on the mobile end will trigger the universal link, And zhihu serious Urlhttps//www.zhihu.com/questions/xxx will not trigger a Universal Link, why zhihu, why not put his Universal primary domain configuration Link, Because of the cross-domain nature of Universal Link.

The general website URL of Zhihu is www.zhihu.com domain name. If you see the problem sharing of Zhihu in wechat moments, you can see such a link if you copy the URL

www.zhihu.com/question/22…

In fact, wechat blocks the Schema, but you can still see a big button to open it in the App. This is indeed achieved through Universal Link. However, if Zhihu has assigned Universal Link to the domain name www.zhihu.com, even if the App has been installed, Universal Link will not work either.

General companies will have their own master domain name, such as zhihu www.zhihu.com. When sharing and spreading everywhere, they also directly share the URL based on the master domain name. However, in order to solve the problem that Apple requires cross-domain to take effect, Universal Link cannot be configured under the master domain name. Therefore, Zhihu will prepare an oia.zhihu.com domain name, specially for Universal Link to use, will not be with any active dissemination of the shared domain name collision, so that in any active WAP pages, can smoothly make Universal Link effective.

Another advantage of cross-domain is that it can break through the restriction of wechat to jump seamlessly from wechat to App.

One simple sentence

Universal Link takes effect only when the url domain of the current WebView is inconsistent with the target URL domain

The 3.2 update

There are two types of update timing for Apple-App-Association:

  • Apple-app-association will be pulled in the first Launch after each App installation

  • Apple-app-association will also be updated in the first Launch after each App version update in Appstore

Therefore, it is useless to restart the APP repeatedly. It is useful to delete the APP and reinstall it, but it is impossible for users to do so. That is to say, once apple-app-Association accidentally wants to recover and make those users feel insensitive, the app will just issue another version

3.3 Universal Link User Behavior

After the Universal Link is triggered, open the App. At this time, there will be a text prompt from XXApp in the upper right corner of the status bar of the App. You can quickly return to the original AP by clicking the text in the status bar

If the user clicks back to wechat, it will be remembered by Apple that the user does not need to jump out of the original App to open a new App, so the Universal Link of the App will be closed and no longer invalid.

If you want to open it, you can go back to safari and open the Universal Link page, and then you get something that looks like Apple’s Smart Bar, and it opens when you click on it

4. Deploy Universal Link services on the H5 terminal

From the perspective of the product manager, the Universal Link forward of THE H5 terminal must meet the following requirements:

  • If the App has been installed, go to the corresponding page

  • If no App is installed, the App download page is displayed

Example of deploying Universal Link on H5:

router.use('/view', function (req, res, next) { var path = req.path; res.redirect('https://www.xxx.com/view' + path + '?xxx=xxx'); });Copy the code

The whole effect is

  • Goto https://www.xxx.com/view/ *

  • Installed App

  • Open the App to trigger handleUniversalLink

  • Go to the /view/ branch and splice the reading page to route the jump

  • Not installed AppWebView

  • Jump in place https://’ ‘www.xxx.com’ ‘/view/*

  • Hit the server’s redirection logic

  • Redirect to https:// ‘ ‘www.xxx.com’ ‘/view/*

  • The CORRESPONDING H5 page is displayed

5. Attachment: Open the App transition page. HTML sample source code

<! DOCTYPE html"><html><head> <meta http-equiv="Content-Type" content="text/html; Charset = UTF-8 "/> <meta name="viewport" content="width=device-width,minimum-scale=1.0"> <body bgColor =" #FFEEDD"> <input type="text" id="acti"> </a></p> </body> <a href=" id="aaa"></a> <script type="text/javascript"> var timeout; function open_iOS_App() { if (isWeiXin()) { open_App(); }else{ open_App(); timeout = setTimeout('open_itunes()', 3000); }; } function open_Android_App() { if (isWeiXin()) { open_android_weixin(); }else{ open_App_Android(); }; } function open_android_weixin() { var acti = document.getElementById("acti").value; var linkUrl = "xxxxxxxxxxxxx://utils? Action = sendIntent ¶ ms = "+ acti; var yingyongbaoUrl = "http://a.app.qq.com/o/simple?pkgname=com.xxxx.xxxxx&android_schema=" + encodeURI(linkUrl); window.location = yingyongbaoUrl; } function open_App() { var ver = (navigator.appVersion).match(/OS (\d+)_(\d+)_? (\d+)? /); ver = parseInt(ver[1], 10); if(ver<9) { if (isWeiXin()) { open_weixin_App(); }else{ open_itunes(); } } else{ var activity = document.getElementById("acti").value;  document.getElementById("aaa").href = "https://joeychang.me/s.html? params="+ encodeURI(activity); // alert(document.getElementById("aaa").href); // alert(activity); document.getElementById("aaa").click(); } } function open_App_Android() { var acti = document.getElementById("acti").value; window.location = "intent://xxxxxxxxx? params="+ acti +"#Intent; package=com.xxxx.xxxxx; scheme=xxxxxxx; launchFlags=268435456; end;" ; {} function open_itunes () / * to open the app store * / window. The location = "http://itunes.apple.com/cn/app/idxxxxxxxx"; } function open_weixin_App() {var acti = document.getelementById ("acti").value; window.location="http://a.app.qq.com/o/simple.jsp?pkgname=com.xxxx.xxxxx&activity=" + acti; *} / * determine whether WeChat browser/function isWeiXin () {var ua = window. The navigator. UserAgent. ToLowerCase (); if(ua.match(/MicroMessenger/i) == 'micromessenger'){ return true; }else{ return false; } } function linktoApp() { var queryStr = decodeURI(window.location.search.substr(1)); var ua = navigator.userAgent.toLowerCase(); if (queryStr.indexOf("}") >= 0 && queryStr.indexOf("{") >= 0) { var activity = queryStr.substring(queryStr.indexOf("{"),  queryStr.lastIndexOf("}") + 1); document.getElementById("acti").value = activity; if (/iphone|ipad|ipod/.test(ua)) { // IOS } else if (/android/.test(ua)) { // Android open_Android_App(); } else {/ / other browser Windows. The location = "http://a.app.qq.com/o/simple.jsp?pkgname=com.xxxxxxx.xxxxxxxx"; }} else {document.getelementById ("acti").value = "error "; Myfun ()*/ window.onload=linktoApp; </script></head><body></body></ HTML >Copy the code