Skip to content

Commit

Permalink
Documentation using MkDocs (#33)
Browse files Browse the repository at this point in the history
* Documentation using  MkDocs

* Update documentation.yaml

* Update documentation.yaml

* mkdocs yml

* remove logo

* Update documentation.yaml
  • Loading branch information
gabrielbmoro authored Oct 20, 2024
1 parent a1b72a5 commit c09e227
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: documentation
on:
push:
branches:
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure Git Credentials
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
114 changes: 114 additions & 0 deletions docs/1-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Setup

This guide presupposes the prior configuration of deeplinks within the native platforms:

- [Android deeplink](https://developer.android.com/training/app-links/deep-linking?hl=pt-br)
- [iOS URL scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)
- [iOS universal link](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)

### Android setup
The library provides two types of initialization (KMP only and Compose), you should use the one that fit your needs.

#### KMP only
On the Android app, inside the `onCreate`, call the extension `RinkuInit()`.

```kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
RinkuInit()
setContent {
App()
}
}
}
```

#### With Compose multiplatform
First make sure you included the `rinku-compose-ext`in your `commonMain`.
On the Android app inside the `setContent` use `ComponentActivity.Riku` extension to wrap the root of your app.

```kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
Rinku {
App()
}
}
}
}
```

### iOS setup

For the iOS platform, deep links are processed within the AppDelegate or SceneDelegate, contingent on the project's configuration. The primary objective is to intercept the platform-specific deep link and relay it as an argument to Rinku's handleDeepLink(url) method.

Example within AppDelegate:

```swift
@UIApplicationMain
class AppDelegate: NSObject, UIApplicationDelegate {
...
// Provide deepLinkFilter and deepLinkMapper if needed
let rinku = RinkuIos.init(deepLinkFilter: nil, deepLinkMapper: nil)
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
rinku.onDeepLinkReceived(url: url.absoluteString)
return true
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL {
let urlString = url.absoluteString
rinku.onDeepLinkReceived(userActivity: userActivity)
}
return true
}
...
}
```

### Common setup
In the common code you just need to listen to the deeplinks and treat them as you need. Once the application (Android or iOS) recieves a deeplink it will parse it into a `Deeplink` data class and pass it into the listener. Use the listener that suite your project.

#### Using Compose

Example RootApp in commonMain:

```kotlin
@Composable
fun RootApp() {
var deepLink by remember { mutableStateOf<DeepLink?>(null) }
DeepLinkListener { deepLink = it }
MainScreen(deepLink)
}
```

#### KMP only
Just use the suspend function `listenForDeepLinks` and react as you will.

Example inside a Decompose component:

```kotlin
class AppComponentImpl(
private val initialStack: List<Config> = emptyList(),
componentContext: ComponentContext,
) : AppComponent, ComponentContext by componentContext {
private val navigation = StackNavigation<Config>()

init {
launch { initDeepLinkListener() }
}

private suspend fun initDeepLinkListener() {
listenForDeepLinks {
navigation.replaceAll(
*it.toScreenStack().toTypedArray()
)
}
}
}

```
11 changes: 11 additions & 0 deletions docs/2-firing-internal-deeplink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Firing Internal Deeplink

To invoke an internal deep link within your application, use the handleDeepLink method provided by Rinku. This method accepts a single parameter: the URL of the deep link you wish to trigger.

```kotlin
Rinku.handleDeepLink("https://test.deeplink/path?query=true")
```

In this example, https://test.deeplink/path?query=true represents the URL of the deep link. The URL scheme, path, and query parameters should be replaced with values that are relevant to your application's routing structure.

By leveraging Rinku's handleDeepLink method, you can enhance your application's navigation capabilities, making it easier to programmatically direct users to specific areas of your app from shared KMP code.
42 changes: 42 additions & 0 deletions docs/3-type-safe-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Type-safe parameters

Rinku supports get and create typesafe parameters leveraging the kotlinx-serialization. In order to use the following functions the app/module needs to setup [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization).

### Getting parameters
Use the `DeepLink` extension `<T> DeepLink.getParameter` and pass the key of the argument in the URL query and the KSerializer of the correspoinding kotlin class.

example:

```kotlin
@Serializable
data class Name(val name: String)

fun example() {
val url = "https://theolm/dev/path?test={"name": "Theo"}"
val deepLink = DeepLink(url)

val param : Name = deepLink.getParameter(name = "wrong name", kSerializer = Name.serializer())
}

```

### Build a URL using Serializable classes
Rinku also provides the helper funcion `Rinku.buildUrl` that facilitates the creation of internal deeplinks with parameters. In order to do that you first need to create the URL and fire the deeplink.

example:

```kotlin
@Serializable
data class Name(val name: String)

fun example() {
val testModel = Name("Testing")
val testParam = DeepLinkParam(
"testParam",
testModel,
Name.serializer()
)
val url = Rinku.buildUrl(TestUrl, testParam)
Rinku.handleDeepLink(url)
}
```
25 changes: 25 additions & 0 deletions docs/4-internal-deeplink-filter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Internal Deeplink Filter

Rinku provides a simple way to filter unwanted external deeplinks.
Instead of filter deeplinks providing specific paths in the AndroidManifest and info.plist you implment the interface `DeepLinkFilter` and pass it into Rinku initialization. With this configuration, when the app recieves a not valid deeplink rinku is not going to handle it. This is usefull to block internal deeplinks from external access without having to include it in platform specific configuration.

### Deeplink mapper
This feature is used to map external deeplinks into internal deeplinks.
Use case 1: Android and ios application has different deeplinks registered in marketing campaigns. The mapper can be used to map the external deeplink to unique internal deeplink, and the application can handle the deeplink accordingly in a unified way.
Use case 2: External deeplink does not have the full path to represent a valid stack. Use the mapper to provide the full stack.

```kotlin
// External deeplink rinku://dev.theolm/screenC/
// The deeplink is missing A and B

// Implement and pass the mapper into Rinku init. This way the external deeplink will be mapped and can be handle in the commonMain.
object ExampleMapper : DeepLinkMapper {
override fun map(url: String): String {
return if (url == "rinku://dev.theolm/screenC/") {
return "rinku://dev.theolm/screenA/screenB/screenC/"
} else {
url
}
}
}
```
10 changes: 10 additions & 0 deletions docs/5-demonstrative-samples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Demonstrative Samples

The library includes two illustrative examples utilizing the foremost multiplatform navigation libraries: [Voyager](https://voyager.adriel.cafe/) and [Decompose](https://github.com/arkivanov/Decompose)

- [Voyager sample](https://github.com/theolm/Rinku/tree/main/samples/voyager)
- [Decompose sample](https://github.com/theolm/Rinku/tree/main/samples/decompose)

*Note: Only the Voyager sample includes an iOS application. Nonetheless, the setup for Decompose would mirror that of Voyager.*

*Note 2: Both examples are using Compose multiplatform.*
15 changes: 15 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Welcome to Rinku

Rinku is a lightweight Kotlin Multiplatform library designed to simplify deep link handling across iOS and Android platforms. By abstracting platform-specific details, Rinku enables developers to manage deep links in a unified manner, enhancing code reuse and maintaining consistency across platforms.

## Summary

### 1. [Setup](./1-setup.md)

### 2. [Firing Internal Deep Links](./2-firing-internal-deeplink.md)

### 3. [Type-safe parameters](./3-type-safe-parameters.md)

### 4. [Internal deeplink filter](./4-internal-deeplink-filter.md)

### 5. [Demonstrative Samples](./5-demonstrative-samples.md)
49 changes: 49 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
site_name: Rinku Documentation
theme:
name: material
font:
text: Merriweather Sans
code: Red Hat Mono
logo: assets/logo.png
favicon: assets/favicon.ico
features:
- navigation.footer
- content.code.annotations # (1)!
palette:
# Dark Mode
- scheme: slate
toggle:
icon: material/weather-sunny
name: Dark mode
primary: green
accent: deep purple

# Light Mode
- scheme: default
toggle:
icon: material/weather-night
name: Light mode
primary: blue
accent: deep orange

markdown_extensions:
- smarty
- codehilite:
guess_lang: false
- footnotes
- meta
- toc:
permalink: true
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.inlinehilite
- pymdownx.magiclink
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.emoji
- pymdownx.details
- pymdownx.tabbed:
alternate_style: true
- tables
- admonition

0 comments on commit c09e227

Please sign in to comment.