How to Add Deep Links in Jetpack Compose?
Tags: android, kotlin, beginners
Published: 2023-05-27 00:00:00

**Step-by-step and easy-to-follow guide to implement deep links in your Android app using Navigation Compose library**

This is part of the Jetpack Compose navigation series:

* [Part 1 - Simple Jetpack Compose Navigation Example](https://vtsen.hashnode.dev/simple-jetpack-compose-navigation-example)
    
* [Part 2 - Compose Destinations - Navigation Library](https://vtsen.hashnode.dev/compose-destinations-navigation-library)
    
* [Part 3 - How to Add Bottom Navigation in Jetpack Compose?](https://vtsen.hashnode.dev/how-to-add-bottom-navigation-in-jetpack-compose)
    
* [Part 4 - How to Add Navigation Drawer in Jetpack Compose?](https://vtsen.hashnode.dev/how-to-add-navigation-drawer-in-jetpack-compose)
    
* **Part 5 - How to Add Deep Links in Jetpack Compose?**
    

A deep link is a hyperlink that takes you directly to a certain screen on your app.

For example, clicking on my blog's URL, the Android OS shows a list of apps that can be used to open the URL as you can see below.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682345118651/1589ff6d-6cdc-4a1d-84ce-f871d7e760cb.png)

***Note:*** *The UI above may look a bit different on different Android OS*

Since [my blog's app](https://vtsen.hashnode.dev/simple-rss-feed-reader-jetpack-compose) supports deep links, it shows up as one of the apps, the first one from the left. If I open it with my blog's app, it takes me to specify screen content within my app. That's the called deep link.

To demonstrate how to implement the deep link, I use this [simple Jetpack Compose navigation](https://vtsen.hashnode.dev/simple-jetpack-compose-navigation-example) as an example. Let's add the deep link.

## Overview

The example app has 4 screens:

* Login Screen
    
* Home Screen
    
* Profile Screen
    
* Search Screen
    

I'm going to add the deep link for each screen in this example app.

## 1\. Add **hostname** in the activity's intent filter

In **AndroidManifest.xml**, add the **&lt;data&gt;, &lt;category&gt;** and **&lt;action&gt;** tags in the activity's intent filter as shown below.

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
    <activity
        ...
        <intent-filter>
            <data android:host="vinchamp77.github.io"
                android:scheme="https" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <action android:name="android.intent.action.VIEW" />
        </intent-filter>

    </activity>
...
</manifest>
```

***Note:*** *Hostname is case-sensitive and needs to be lowercase characters.*

## 2\. Add NavDeepLink in NavGraphBuilder.composable()

The following code in **NavGraph.kt** needs to be updated by adding the `deepLinks` parameter when calling the `NavGraphBuilder.composable()`.

```kotlin
// Login Screen
navGraphBuilder.composable(
    route = NavRoute.Login.path,
    deepLinks = listOf (
        navDeepLink {
            uriPattern = "https://vinchamp77.github.io/deeplink/login"
            action = Intent.ACTION_VIEW
        }
    ),
) {
    /*...*/
}

// Home Screen
navGraphBuilder.composable(
    route = NavRoute.Home.path,
    deepLinks = listOf (
        navDeepLink {
            uriPattern = "https://vinchamp77.github.io/deeplink/home"
            action = Intent.ACTION_VIEW
        }
    ),
) {
    /*...*/
}

// Profile Screen
navGraphBuilder.composable(
    route = NavRoute.Profile.withArgsFormat(NavRoute.Profile.id, NavRoute.Profile.showDetails),
    deepLinks = listOf (
        navDeepLink {
            uriPattern = "https://vinchamp77.github.io/deeplink/profile?id={id}&showDetails={showDetails}"
            action = Intent.ACTION_VIEW
        }
    ),
    /*...*/
) {
    /*...*/

}

// Search Screen
navGraphBuilder.composable(
    route = NavRoute.Search.withArgsFormat(NavRoute.Search.query),
    deepLinks = listOf (
        navDeepLink {
            uriPattern = "https://vinchamp77.github.io/deeplink/search?query={query}"
            action = Intent.ACTION_VIEW
        }
    ),
    /*...*/
) {
    /*...*/
}
```

You build the list of `NavDeepLink` using `navDeepLink()` helper function by specifying the `uriPattern` and the `action` parameters.

URI pattern without any argument is straightforward, no big deal. However, when the **URI pattern has arguments**, it must follow the **standard URL query parameters** format.

It took me a while to figure this out. For example, the following URI pattern won't work.

```kotlin
 uriPattern = "https://vinchamp77.github.io/deeplink/search/{query}"
```

It works only if you have one deep link defined. If there is more than one deep link, it falls back to the first deep link that you defined.

So it must be in this format.

```kotlin
uriPattern = "https://vinchamp77.github.io/deeplink/search?query={query}"
```

## 3\. Test the Deep Link

The easiest way to test the deep link is to use the **Tools -&gt; App LInks Assistant** in Android Studio.

Go to **step 4 - Test on device or emulator** directly.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682402215746/370fc4da-5a61-4ac9-8d7a-ccf1a168d40f.png)

Then, add the URL that you would like to test and click **Run Test**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682402487671/5843a916-3343-4a36-841e-c5b86548228a.png)

*Note: These are equivalent adb commands.*

```kotlin
$ adb shell setprop log.tag.AppIndexApi VERBOSE
$ adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d 'https://vinchamp77.github.io/deeplink/search?query="liang moi"'
```

Click **JUST ONCE** or **ALWAYS** to open with Simple Navigation Compose App.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682402604924/81c421aa-a919-4ef1-96f7-92268b47cc7f.png)

The search screen is shown with the search query that we passed in from the URL.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682402744613/54b893ec-8f4b-44e6-b655-3c5f2d3470c5.png)

You're not done! Deep links are finally implemented for your app.

## 4\. Verify Deep Link Host Name

Well, not really. If you run this app on Android 12 (API level 31). The link doesn't take you to the app at all. It requires you to verify the deep links' hostname belongs to your app.

To do that, you first need to generate a Digital Asset Links file and upload it to your as specify location on your website.

* &lt;your hostname&gt;/.well-know/assetlinks.json
    

Now, proceed to **step 3 - Associate website** in the App Links Assistant

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682405962039/c8a43e87-57a3-4946-8ff8-0f183b81ac06.png)

A lot of info should be already pre-filled for you. Select **Signing config -&gt; Debug**, and click **Generate Digital Asset Links File.**

*For a production build, select* ***Select keystore file*** *instead*

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682406353174/52f29a87-3805-4b4d-88cc-56fc03e63e6e.png)

Once the file is generated, you can click Save file to download the file. You need to copy the file to your website. In this example, the file location is

* [https://vinchamp77.github.io/.well-known/assetlinks.json](https://vinchamp77.github.io/.well-known/assetlinks.json)
    

> If you are using a &lt;username&gt;.github.io page like mine, there are some things you need to be aware of for the .well-known folder to be recognized.

**Method 1** - If you **have \_config.yml**, you need to add the **include: \[".well-known"\]**. This is an example of my github page's \_config.yml.

```yaml
theme: jekyll-theme-minimal
title: Vincent Tsen
description: Self-taught Hobbyist Native Android Kotlin Developer
include: [".well-known"]
```

**Method 2** - If you **don't have** **\_config.yml**, you add **.nojekyll** in your root directory\*\*.\*\* The .nojekyII is just an empty file without any content.

Click **Link and Verify** and you should see

> Success! Your app is associated with the selected domain(s).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682423859536/934ca153-e044-4530-aad2-174afd1337c0.png)

The Android Studio also automatically added the **autoVerify** to the intent filter elements.

```xml
<intent-filter android:autoVerify="true">
    <data android:host="vinchamp77.github.io"
        android:scheme="https" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <action android:name="android.intent.action.VIEW" />
</intent-filter>
```

Try to test the app link in step 3 above on Android 12 (API level 31) and above, it works!

You may also notice that it **doesn't prompt you to choose from a list of apps, but instead takes you directly to your app**. This is also the benefit of verifying your hostname.

## Summary

Deep link is quite useful because it brings you directly to a specific screen. One thing I noticed is if the URL doesn't match any of the URL patterns, it falls back to the first deep link. For example, the first deep link in this example is the login screen. All the invalid URL brings you to the login screen directly.

Other than triggering a deep link from URL, you can also trigger the deep link from another app using `PendingIntent`. I will cover the `PendingIntent` topic later on. Enjoy!

## **Source Code**

GitHub Repository: [Demo\_SimpleNavigationCompose (deep link branch)](https://github.com/vinchamp77/Demo_SimpleNavigationCompose/tree/deeplink)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/how-to-add-deep-links-in-jetpack-compose)*.