Type Safe Navigationの導入でやらかした話

# 初めに
公開中のアプリに、型安全のNavigationを導入しリリースしたところ、更新した全ユーザーにクラッシュが発生してアプリが起動できなくなってしまいました。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115846/0ecb2efd-5d0c-ad9c-9632-006f937fd261.png" width="48%"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115846/8dd9d487-6b15-20b9-d99d-829af47c438f.png" width="48%">

# 調査
## エラーの内容
```
D7.g: Serializer for class 'k' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
```
"Serializer対象のクラスが見つかりません。@Serializableを付け、serialization pluginを適用していることを確認してください。"とのことですが、どちらも適用されています。
ただし、クラスが見つからないのと、実装時の動作確認では問題無かったので、`Proguard`絡みだと予想されます。

```diff:build.gradle.kts
buildTypes {
    release {
        isMinifyEnabled = false
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
    }
+    debug {
+        isMinifyEnabled = true
+        isShrinkResources = true
+        proguardFiles(
+            getDefaultProguardFile("proguard-android-optimize.txt"),
+            "proguard-rules.pro"
+        )
+    }
}
```
設定を追加して、ローカルのデバッグビルドでも発生するのか確認します。
```
kotlinx.serialization.SerializationException: Serializer for class 'SearchGraph' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
```
デバッグ情報が付与されているため、見つからないというクラス名も出力されていますが、同じエラーが発生したので、こちらが発生しないよう調査、修正を行います。

## 導入手順の再確認
[Kotlin DSL と Navigation Compose における型安全性 &nbsp;|&nbsp; Android Developers](https://developer.android.com/guide/navigation/design/type-safety?hl=ja)
こちらの手順を確認しましたが、こちらも問題ありませんでした。また、`Proguard`に関する記述はありませんでした。

`Proguard`に関する情報を探すと、`Kotlin Serialization`側で見つかりましたが、プラグインを導入すると自動的に適用されるようです。実装方法によっては追加の設定が必要なようですが、そちらにも該当しませんでした。
[Kotlin/kotlinx.serialization: Kotlin multiplatform / multi-format serialization](https://github.com/Kotlin/kotlinx.serialization?tab=readme-ov-file#android)

## 他のアプリで確認
`Codelab`やGoogleが公開しているサンプルアプリを探してみましたが、`Type Safe Navigation`を使用している物はみつかりませんでしたが、先日投稿した記事で参考にしたサンプルが`Type Safe Navigation`を使用していたので、そちらの`Proguard`を有効にして実行してみたところ、エラーは発生せずに起動できました。

## 原因箇所の特定
`Type Safe Navigation`+`sealed interface(?)`＋`Hilt(?)`＋`Proguard`で、正常に動いているアプリと動かないアプリの設定や実装の違いを比較し、何処が原因なのか調査しました。

結論としては、現在の実装方法では、`NavHost`内で、ネストしたナビゲーション`navigation<T>()`を使用せずに、通常のナビゲーション`composable<T>()`を使用すると、デスティネーションのクラスが見つからなくなるようです。現在の実装方法ではと書いたのは、通常のナビゲーションを使用していても問題無く動作しているアプリもあるのですが、それらのアプリでは、デスティネーションの指定に`sealed interface`を使用しなかったり、`Hilt`を使用していなかったりするため、そのあたりも影響があるようです。
```diff_kotlin:Destination.kt
sealed interface Destination {
    // ...
+    @Serializable
+    data object DemoScreen: Destination
}
```
```diff_kotlin:MainActiviy.kt
    NavHost(
        navController = navController,
        startDestination = navigator.startDestination,
        modifier = Modifier.padding(innerPadding)
    ) {
+        composable<Destination.DemoScreen>() {
+            // TODO
+        }
        navigation<Destination.AuthGraph>(
            startDestination = Destination.LoginScreen
        ) {
            composable<Destination.LoginScreen> {
                val viewModel = hiltViewModel<LoginViewModel>()
```
```
kotlinx.serialization.SerializationException: Serializer for class 'DemoScreen' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
```
# 修正方法
## その1
デスティネーションを定義しているクラスを最適化の対象から除外する。
```diff:proguard-rules.pro
+-keep class jp.hamachi.android.img.core.navigation.** {*;}
```
## その2
デスティネーションを定義している`sealed interface`にも@Serializableを付ける。
```diff_kotlin:Destination.kt
+@Serializable
sealed interface Destination {
```
# 終わりに
今回の場合は上記のどちらの対応でもエラーは発生しなくなりましたが、ネストしたナビゲーションでは元々発生せず、通常のナビゲーションの場合のみ発生する理由がはっきりしないままです。ネストしたナビゲーションを使用することが推奨されていたりするのでしょうか？ご存じの方がいらっしゃいましたらお教えください。
# 戒め
リリース前には、リリースビルドで動作確認を行いましょう。