10

There are too much Deep Linking (Universal Links or App Links) tutorials. But most of them shows how to enable it in Android or IOS Apps. Also there are paid cloud solutions but they offer to much features. But there are three main problems I faced in real life:

  1. Some browsers doesn’t allow App Links to work. For example you can configure http://example.com to be caught in app, but if this link is clicked by user through Facebook app it is not handled, and Facebook browser shows the web site.
  2. There is no unique standard solution to handle links both for Android and IOS apps.
  3. No practical solution if the App is not installed on mobile device and user clicks an App Link.

I wrote this Q&A which is the result of my studies (spent too many hours) to have a unique and working for all cases solution.

The codes are coming from my working solution, but I removed some parts just to show the idea. If there are some compiling problems, please follow the algorithm and write your own code

Here is the solution, go step by step even if you know some steps, because there are tricks in codes. Also some explanations are in comment lines of the code parts, please read them.

Examples are to handle deeplink http://example.com/v/ by your Android and IOS apps with an argument at the end, for example http://example.com/v/id-of-user?key=value.

1. Configuring Android

1.1 Add activity information to your AndroidManifest.xml file:

<activity
android:name=".appLinkHandlerActivity">

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
        android:host="example.com"
        android:pathPrefix="/v/"
        android:scheme="http" />
</intent-filter>

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

<!—this intent is needed to handle links to myapp://share, I will explain later why we need it -->
    <data
        android:host="share"
        android:scheme="myapp" />
</intent-filter>

1.2 Create an activity named appLinkHandlerActivity which will handle the links clicked

    public class appLinkHandlerActivity extends AppCompatActivity {


    /* assume that user is clicked http://example.com/v/my-user-id   
    actCode will be “v”, pCode will be “my-user-id” */
    String actCode="", pCode="";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        // ATTENTION: This was auto-generated to handle app links.
        Intent appLinkIntent = getIntent();
        String appLinkAction = appLinkIntent.getAction();
        Uri appLinkData = appLinkIntent.getData();



        String code = null;
        try {
            code = getIntent().getData().getLastPathSegment();
        } catch (Exception e) {

        }

        if (code == null) {
            Intent i = new Intent(this, {your main activity.class});
            startActivity(i);
        }

        List<String> params=appLinkData.getPathSegments();


        if (params.size()>0)
            actCode=params.get(0);

        if (params.size()>=2)
            pCode=params.get(1);

        /* assume that user is clicked http://example.com/v/my-user-id actCode is “v”, pCode is “my-user-id”  Do now whatever you need. */
    } 
 }

2. Configuring IOS

This is more complex than Android, I will explain the necessary points here. Please refer to documents: https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1

https://www.raywenderlich.com/128948/universal-links-make-connection

2.1 You have to enable Associated Domains while creating an App ID on Apple Developer Portal. Important issue: you need to have a purchased Apple Developer Account to enable this option, that means without purchasing a developer account, you can’t try AppLinks on your IOS project.

2.2 In XCode project, open “Capabilites” tab and switch Associated Domains to On. If you didn’t enable Associated Domains in Apple Developer Portal App ID section, this might not be selectable Add an entitlement by clicking on + button under Associated Domains option, write “applinks:example.com”.

2.3 Create a file on your web server named “apple-app-site-association” and this file must be accessed through https://example.com/apple-app-site-association HTTPS is mandatory and if it is not a valid SSL certificate App Link might not work. Add following lines into apple-app-site-association file:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "6HY7TF56.com.example.app",
                "paths": [ "/ios/*", "/v/*" ]
            }
        ]
    }
}

appID is format of {“Team ID”.”Bundle ID of your App”}. You can find your teamID under Membership Details section at Developer Portal.

We handle the link http://example.com/v/parameters, but here you see there is another path configuration for “/ios/*”. It is needed to bypass unsupported browsers, I will explain later.

2.4 In AppDelegate.m file add two methods to handle the user clicks on example.com

-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
    if ([userActivity.activityType isEqualToString: NSUserActivityTypeBrowsingWeb]) {

        NSURL *url = userActivity.webpageURL;
          [self parseUrl:url];

    }
return YES;
}


- (void) parseUrl:(NSURL * )handledUrl {
    NSString *urlStr=@"";
    NSString *pCode=@"";
    NSString *handledUrlStr=[handledUrl parameterString];

    NSString *handledUrlQueryPart;

    NSArray<NSString *> *pathsArray= [handledUrl pathComponents];

    //remember that we only added paths “/v/*” and “/ios/*” to handle in apple-app-site-association file. If you want to handle more subpaths you can add them into apple-app-site-association file, then below if-else conditions. Don’t touch to config and code for “/ios/*” path, it is needed to bypass unsopported browsers.
    if ([pathsArray[1]  isEqual: @"v"]){
        //sample url= http://example.com/v/menu?aaa=bbb
        pCode = pathsArray[2];
        handledUrlQueryPart=[handledUrl query];
    } else if ([pathsArray[1]  isEqual: @"ios"]){
        //sample url= http://example.com/ios/deeplink-ios.php?/v/menu?aaa=bbb
        NSArray *uriArray = [[handledUrl query] componentsSeparatedByString:@"?"];
           NSArray *queryPathsArray = [uriArray[0] componentsSeparatedByString:@"/"];
        if ([queryPathsArray count] > 2)
            pCode = queryPathsArray[2];

        if ([uriArray count] > 1 ){
            handledUrlQueryPart=uriArray[1];
        }
    }

    /* here pCode is the parameter what is passed from user. If the link clicked is http://example.com/v/menu it is “menu”. If the link clicked is http://example.com/v/menu?aaa=bbb it is “menu?aaa=bbb”. So you can do now what ever you need. */    
}

3. Managing the unacught clicks.

3.1 Ok, Your android and IOS apps should handle the clicks on link http://example.com/v/blabla and pass the “blabla” parameter to pCode variable used in the methods I showed. But some browsers like Facebook app may disable App Links to work. In this case user click goes to your web server and the browser tries to show the content of http://example.com/v/blabla which is probably 404 Page Not Found. To handle these clicks we will configure Apache web server and redirect users to your App. If you use IIS or another, I don’t know how to do it, but you can take this as sample and use same algortihm to configure your web server.

3.2 Write the below lines in .htaccess file in root directory of example.com

#redirect to deeplink
<IfModule mod_rewrite.c>

#if there is a request to example.com/v/any-sub-path, redirect them to example.com/deeplink.php file. This rule is for both IOS and Android
RewriteRule ^(v)/.* /deeplink.php [L]

#if there is a request to example.com/ios/any-sub-path, redirect them to app installation page. That means your app is not installed on IOS device. This rule is for IOS devices only
RewriteRule ^(ios)/.* http://itunes.apple.com/install-of-your-app [L] 
</IfModule>  

4. Redirect users to Apps

4.1 The redirection rules in .htaccess file shown at step-3 redirects users to deeplink.php file. So here is the content of that file to redirect users to your App.

 <?php

$request_uri=$_SERVER[REQUEST_URI];

$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
if(stripos($ua,'android') == true){
//if user device is android, redirect it to intent url which is handled by Android.
        $redir_tag="<meta http-equiv='refresh' content='0;url=intent://share$request_uri/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app;end' />";
//scheme=myapp and host named “share” is configured in AndroidManifest.xml file to be handled by the activity.
//fallback url is the url, if your app is not installed on android device, so you can redirect them to a page to install android app. In some cases users are redirected to Play Store directly for application id of com.example.app
}

else if ( (stripos($ua,'iPhone') == true) || (stripos($ua,'iPad') == true) ) {
        //if user device is IOS, redirect them to a web page to see. There will be a link here to the another handled link: http://example.com/ios/blabla.
        // due to my experience there is no way to redirect IOS to app directly at this stage, user must click a link on browser and that link must be different than the link which was shown and clicked at first.
        // another experience taught me ther ecan be problems if we redirect users to a web page under example.com which is configured as applink, so I redirect users to a page under deep.example.com here
        $redir_tag="<meta http-equiv='refresh' content='0;url=http://deep.example.com/deeplink-ios.php?$request_uri' />";
}

else {
//If the device is neither IOS nor Android, redirect users to a web page which gives information that this link is for Android and IOS only
        $redir_tag="<meta http-equiv='refresh' content='0;url=http://example.com/non-mobile' />";
}


?>



<html>
    <head>

        <!— add tags for no-caching, this is important, the date below is my son’s birth time and date, he is now 6, so you can use it as a date in the past -->
        <meta http-equiv="cache-control" content="max-age=0" />
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="-1" />
        <meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
        <meta http-equiv="pragma" content="no-cache" />


        <?php echo $redir_tag; ?>

    </head>
</html>

5. Show IOS users a link to click

5.1 Here is the content of http://deep.example.com/deeplink-ios.php file. Users will see a page like below, when they clicked on the link, that request should be handled by your IOS app.

<?php
//we create a link to http://example.com/ios/… which is configure to be handled by IOS app. IOS needs to be a user click in some cases to handle the request, that is why this page is shown to the user
$request_uri=$_SERVER[REQUEST_URI];
$link="<div class='w3-container w3-card'><h1><a href='http://example.com/ios$request_uri'>Click here to open MyApp</a></h1></div>";
?>

<html>
    <head>

        <!—adding no-cache tags is also important here-->
        <meta http-equiv="cache-control" content="max-age=0" />
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="-1" />
        <meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
        <meta http-equiv="pragma" content="no-cache" />
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3mobile.css">

    </head>
<body>

<?php echo $link ?>

</body>
</html>

6. Case Analysis:

6.1 Android

6.1.1 Case-1 – app is installed on device
• Browser requests for http://example.com/v/blabla
• Android catches the link and create the activity configured in your manifest file

6.1.2 Case-2 - app is installed on device
• Browser requests for http://example.com/v/blabla
• Android can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is android, and redirect to: intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app
• Android catches the request which is for intent://, so due to configuration in manifest file myapp://share/v/blabla is handled by the activity

6.1.3 Case-3 - app is not installed
• Browser requests for http://example.com/v/blabla
• Android can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is android, and redirect to: intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app
• Android catches the request which is for intent://, but there is no app installed for id: com.example.app. It falbacks and redirect browser to http://example.com/get-app or Play Store installation page of your app in some cases

6.2 IOS

6.2.1 Case-1 – app is installed on device
• Browser requests for http://example.com/v/blabla
• IOS catches the link and call the continueUserActivity method in AppDelegate.m

6.2.2 Case-2 – app is installed on device
• Browser requests for http://example.com/v/blabla
• IOS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is IOS, and redirects to: http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php file shows a URL to user. URL is: http://lify.me/ios/v/blabla
• User clicks the URL, and browser requests for http://lify.me/ios/v/blabla
• IOS catches the request due to configuration for path “/ios/*” in apple-app-site-association file and call the continueUserActivity method in AppDelegate.m
• If IOS can’t catch the request for http://lify.me/ios/v/blabla with any reason, it will behave as app is not installed on device. See that case.

6.2.3 Case-2 – app is not installed on device
• Browser requests for http://example.com/v/blabla
• IOS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is IOS, and redirects to: http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php file shows a URL to user. URL is: http://lify.me/ios/v/blabla
• User clicks the URL, and browser requests for http://lify.me/ios/v/blabla
• If IOS can’t catch the request for http://lify.me/ios/v/blabla
• Browser connects to web server, request for /ios/v/blabla
• It is redirected to http://itunes.apple.com/install-of-your-app due to configuration in .htaccess file on web server

6.3 App Link is clicked on non Android or IOS device
• Browser requests for http://example.com/v/blabla
• Device OS can’t catch the link.
• Browser connects to web server, request for /v/blabla
• It is redirected to deeplink.php?/v/blabla due to configuration in .htaccess file
• deeplink.php learns it is nor IOS neither Android, and redirects to: http://example.com/non-mobile

Mustafa Atalar
  • 101
  • 1
  • 4
  • There are free solutions to implement deep linking. My team working on Firebase Dynamic Links. I am aware about at least one competitor that is also free. Not sure what you mean by "but they offer to much features". You can ignore features you don't need. – Oleksiy Ivanov Sep 18 '17 at 18:14
  • I'm sure you've already done your research, but [Branch](http://branch.io) and Firebase do this for free. These products, Branch especially, focus on handling all of these edge cases so that you don't have to. There's no reason to build this all yourself when its already open source. – clayjones94 Sep 18 '17 at 19:17
  • firebase dynamic linking not working with facebook an d messenger applications. – tabebqena Oct 02 '19 at 17:05
  • @clayjones94 am not sure if Branch was for free back at the time you wrote the comment, but now it's certainly not. – MAS Jun 29 '20 at 12:21

0 Answers0