Skip to content

Commit eb35908

Browse files
committed
Add deeplink-guide
Guide that summarizes the content for basic and advanced deep link recipe
1 parent 2e00857 commit eb35908

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

docs/deeplink-guide.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Deep Link Guide
2+
3+
This guide explains how to handle deep links with Navigation 3. It covers parsing
4+
intents into navigation keys and managing back stacks for proper "Back" and "Up"
5+
navigation behavior.
6+
7+
## 1. Parsing an intent into a navigation key
8+
9+
When your app receives a deep link, you need to convert the incoming `Intent`
10+
(specifically the data URI) into a `NavKey` that your navigation system can
11+
understand.
12+
13+
This process involves four main steps:
14+
15+
1. **Define Supported Deep Links**: Create `DeepLinkPattern` objects that
16+
describe the URLs your app supports. These patterns link a URI structure to
17+
a specific `NavKey` class.
18+
2. **Parse the Request**: Convert the incoming `Intent`'s data URI into a
19+
readable format, such as a `DeepLinkRequest`.
20+
3. **Match Request to Pattern**: Compare the incoming request against your list
21+
of supported patterns to find a match.
22+
4. **Decode to Key**: Use the match result to extract arguments and create an
23+
instance of the corresponding `NavKey`.
24+
25+
### Example Implementation
26+
27+
The `com.example.nav3recipes.deeplink.basic` package provides an example of
28+
this flow.
29+
30+
**Step 1: Define Patterns**
31+
32+
In a file (e.g., `MainActivity`), define a list of supported deeplink patterns.
33+
You can leverage the `kotlinx.serialization` library by annotating your Navigation keys with
34+
`@Serializable`, and then use the generated `Serializer` to map deep link arguments to a
35+
key argument.
36+
37+
For example:
38+
39+
```kotlin
40+
internal val deepLinkPatterns: List<DeepLinkPattern<out NavKey>> = listOf(
41+
// URL pattern with exact match: "https://www.nav3recipes.com/home"
42+
DeepLinkPattern(HomeKey.serializer(), (URL_HOME_EXACT).toUri()),
43+
44+
// URL pattern with Path arguments: "https://www.nav3recipes.com/users/with/{filter}"
45+
DeepLinkPattern(UsersKey.serializer(), (URL_USERS_WITH_FILTER).toUri()),
46+
47+
// URL pattern with Query arguments: "https://www.nav3recipes.com/users/search?{firstName}&{age}..."
48+
DeepLinkPattern(SearchKey.serializer(), (URL_SEARCH.toUri())),
49+
)
50+
```
51+
The sample `DeepLinkPattern` class takes the defined URL pattern and serializer for the associated
52+
key, then maps each argument ("{...}") to a field in the key. Using the serializer,
53+
`DeepLinkPattern` stores the metadata for each argument such as its KType and its argument name.
54+
55+
56+
**Step 2: Parse the Request**
57+
58+
In your Activity's `onCreate` method, you will first need to retrieve the data URI from the
59+
intent. Then you should parse that URI into a readable format. In this recipe, we use a
60+
`DeepLinkRequest` class which parses the URI's path segments and query parameters into a map. This
61+
map will make it easier to compare against the patterns defined in Step 1.
62+
63+
**Step 3: Match Request to Pattern**
64+
65+
Once the request is parsed, iterate through your list of `deepLinkPatterns`. For each pattern,
66+
use a matcher (e.g., `DeepLinkMatcher`) to check if the request's path and arguments align with
67+
the pattern's structure. The matcher should return a result object containing the matched arguments
68+
if successful, or null if not.
69+
70+
**Step 4: Decode to Key**
71+
72+
If a match is found, use the matched arguments to instantiate the corresponding `NavKey`.
73+
Since we used `kotlinx.serialization` in Step 1, we can leverage a custom Decoder (`KeyDecoder`)
74+
to decode the map of arguments directly into the strongly-typed key object.
75+
76+
```kotlin
77+
override fun onCreate(savedInstanceState: Bundle?) {
78+
super.onCreate(savedInstanceState)
79+
80+
val uri: Uri? = intent.data
81+
82+
val key: NavKey = uri?.let {
83+
// Step 2: Parse request
84+
val request = DeepLinkRequest(uri)
85+
86+
// Step 3: Find match
87+
val match = deepLinkPatterns.firstNotNullOfOrNull { pattern ->
88+
DeepLinkMatcher(request, pattern).match()
89+
}
90+
91+
// Step 4: Decode to NavKey
92+
match?.let {
93+
KeyDecoder(match.args).decodeSerializableValue(match.serializer)
94+
}
95+
} ?: HomeKey // Fallback to home if no match or no URI
96+
97+
setContent {
98+
val backStack = rememberNavBackStack(key)
99+
// ... setup NavDisplay
100+
}
101+
}
102+
```
103+
104+
For more details, refer to the [Basic Deep Link Recipe](../app/src/main/java/com/example/nav3recipes/deeplink/basic/BDEEPLINKREADME.md)
105+
and the `MainActivity` in `com.example.nav3recipes.deeplink.basic`.
106+
107+
## 2. Building a synthetic backstack & managing a Task stack
108+
109+
Handling deep links isn't just about showing the correct screen; it's also about
110+
ensuring the user has a natural navigation experience when they press "Back" or
111+
"Up". This often requires building a synthetic back stack.
112+
113+
### The Problem: New Task vs. Existing Task
114+
115+
When a user clicks a deep link, your activity might launch in a new Task or in
116+
an existing one.
117+
118+
* **Existing Task**: If an app is already open, the deep link might just
119+
push a new screen onto the current stack. The "Back" button should return
120+
the user to where they were before the deep link.
121+
* **New Task**: If your app wasn't already open (or if the intent flags forced a new
122+
task), there is no existing backStack. Yet the "Back" button should conceptually go "up" to
123+
the parent of the current screen, rather than exiting the app.
124+
125+
### Solution: Synthetic Back Stack
126+
127+
A synthetic back stack is a manually constructed list of keys that represents
128+
the path the user *would* have taken from the root screen to the current screen.
129+
130+
**Strategy**
131+
132+
1. **Identify the Context**: Determine if you are in a new task or an existing
133+
one. Intent flags like `FLAG_ACTIVITY_NEW_TASK` would signal a new task.
134+
2. **Construct the Stack**:
135+
* **Up Button**:
136+
* In an **Existing Task**, you will need to restart the Activity in a new Task
137+
and build a backStack to ensure this parent exists
138+
* In a **New Task**, "Back" should behave like "Up" (go to the
139+
parent). You will need to build a backStack to ensure this parent exists.
140+
* **Back Button**:
141+
* In an **Existing Task**, "Back" goes to the previous screen (the one
142+
that triggered the deep link).
143+
* In a **New Task**, "Back" should behave like "Up" (go to the
144+
parent). You will need to build a backStack to ensure this parent exists.
145+
146+
### Task & backStack illustration
147+
148+
**Existing Task**
149+
150+
| Task | Target | Synthetic backStack |
151+
|-------------|-----------------------------|-------------------------------------------------|
152+
| Up Button | Deep linked Screen's Parent | Restart Activity on new Task & build backStack |
153+
| Back Button | Screen before deep linking | None |
154+
155+
**New Task**
156+
157+
| Task | Target | Synthetic backStack |
158+
|-------------|-----------------------------|------------------------------------------------|
159+
| Up Button | Deep linked Screen's Parent | Build backStack on Activity creation |
160+
| Back Button | Deep linked Screen's Parent | Build backStack on Activity creation |
161+
162+
**Implementation Tips**
163+
164+
* **Restarting for consistency**: If you want to enforce a specific back stack
165+
structure (like always having the app's home screen at the bottom), you
166+
might need to restart the Activity with `Intent.FLAG_ACTIVITY_NEW_TASK` and
167+
`Intent.FLAG_ACTIVITY_CLEAR_TASK` when handling a deep link, then build the
168+
full stack manually.
169+
170+
For a comprehensive demonstration of simulating "App A" deep linking into "App
171+
B" and managing these stacks, see the [Advanced Deep Link Recipe](../app/src/main/java/com/example/nav3recipes/deeplink/advanced/ADEEPLINKREADME.md)
172+
and the module `com.example.nav3recipes.deeplink.app`.

0 commit comments

Comments
 (0)