-
Notifications
You must be signed in to change notification settings - Fork 0
Navigation: Deeplinks
Deeplinks are just plain black-box config parameters. To make the app handle them, define top level-config for the App entry point.
@Immutable
data class AppConfig(val deeplink: String? = null)
And pass the config to the AppCoordinator via AppScope.
@Composable
fun App(appDependencies: AppDependencies, config: AppConfig) {
val appScope = rememberScope { AppScope(appDependencies, config) }
//...
}
In the coordinator’s init method call navigator with corresponding deeplink param.
class AppCoordinator(
//..
private val config: AppConfig
) : Coordinator() {
init {
if (config.deeplink != null) {
navigator.navigateTo(config.deeplink)
}
//..
}
}
Now the navigation logic is ready to open any deeplink coming as a String parameter. The last step is to create an unique Route in the form of deeplink that will refer to a particular navigation destination.
sealed interface Ticketing : AppRoutes {
data object Flow : Ticketing {
const val RoutePattern: String = "movie://app/ticketing/{movieName}"
fun routeWithParam(movieName: String) = "movie://app/ticketing/${movieName}"
}
}
Then assign the Route to the corresponding scene:
@Composable
fun App(appDependencies: AppDependencies, config: AppConfig) {
//...
NavigationFlow(/* .. */ ) {
//..
dialog(route = AppRoutes.Ticketing.Flow.RoutePattern) { entry ->
// Scene @Composable content goes here
appScope.ticketingFactory.CreateTicketingFlow(
modifier = Modifier.fillMaxSize(),
io = appScope.ticketingFlowIO,
config = TicketingConfig(
// retrieve {movieName} parameter from a deeplink
movieName = requireNotNull(entry.path<String>("movieName"))
)
)
}
//..
}
}
That’s it, now the common code is ready to perform a scene by a deeplink.
To make it work on Android. Specify the corresponding intent filter into AndroidManifest.xml
<intent-filter
android:autoVerify="true"
android:label="Deeplink">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="app"
android:scheme="movie" />
</intent-filter>
In MainActivity.kt, pass the intent param to the AppConfig()
class MainActivity : BlackboxActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
// You can add additional checks to filter out the deeplink data
val action: String? = intent?.action
val deeplinkUri = intent?.data?.toString()
setContent {
CreateMovieApp(AppConfig(deeplink = deeplinkUri))
}
}
}
Test the deeplink via terminal command:
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "movie://app/ticketing/Barbie"
Define corresponding scheme in Target’s Info, e.g movie://
In your AppSceneDelegate retrieve urlContext and pass the deeplink in app controller like so:
// Will be called when app is not running
override fun scene(
scene: UIScene,
willConnectToSession: UISceneSession,
options: UISceneConnectionOptions
) {
//...
val urlContexts = options.URLContexts as? Set<UIOpenURLContext>
val deeplink = urlContexts?.firstOrNull()?.URL?.absoluteString
val appController = createMovieAppController(
nsUserActivity = activity,
config = AppConfig(deeplink = deeplink)
)
//..
}
// Will be called when app is in runtime
override fun scene(scene: UIScene, openURLContexts: Set<*>) {
val urlContexts = openURLContexts as? Set<UIOpenURLContext>
val deeplink = urlContexts?.firstOrNull()?.URL?.absoluteString
window?.rootViewController = createMovieAppController(
nsUserActivity = null,
config = AppConfig(deeplink = deeplink)
)
}
Test the deeplink via terminal command:
xcrun simctl openurl booted "movie://app/ticketing/Barbie"