Dagger-Hiltを取り除いてViewModelを手動でDIする方法を動画で学ぶ

# 手動でDIを実現する方法 + Daggerを取り除く

Dagger-Hiltを使えば、`viewModule()`関数で任意の`ViewModel`インスタンスを生成・取得できますが、次の動画では、`ViewModelProvider.Factory`を使って手動で生成・取得を実現する方法を紹介しています。
<table><tr><td><a href="https://youtu.be/eX-y0IEHJjM">YouTube - Full Guide to Manual Dependency Injection + Removing Dagger</a>
</td><td>
<iframe src="https://www.youtube.com/embed/eX-y0IEHJjM?si=Kht9Qs0EE9loaIJU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</td></tr></table>


**Dagger-Hiltを使ったDIの例（元となるソースコード）**

https://github.com/philipplackner/ManualDependencyInjection/tree/initial_with_dagger

**手動でDIを実現する方法（変更されたソースコード）**

https://github.com/philipplackner/ManualDependencyInjection/tree/manual_di

# 構造

## AndroidManifest

applicationに`MyApp`クラスを、activityに`MainActivity`クラスを指定しています。

```app\src\main\AndroidManifest.xml
    <application android:name=".MyApp" ... >
        <activity android:name=".MainActivity" ... >
            ...
        </activity>
    </application>
```

## クラス図

`Application`、`ComponentActivity`、`ViewModel`、`AuthRepository`に関するクラスを次のクラス図に示します。

```plantuml

'----------------------------
' 左側のApplication以下
'----------------------------

abstract Application
class MyApp
Application <|-- MyApp

MyApp *-- AppModule
MyApp ..> AppModuleImpl

interface AppModule
class AppModuleImpl
AppModule <|-- AppModuleImpl

AppModuleImpl *-d-- AuthApi
AppModuleImpl *-r- AuthRepository
AppModuleImpl ..> AuthRepositoryImpl

interface AuthApi
class retrofit2.http
AuthApi .l.> retrofit2.http


'----------------------------
' 右側のComponentActivity以下
'----------------------------

abstract ComponentActivity
class MainActivity
ComponentActivity <|-- MainActivity

MainActivity .l.> MyApp

MainActivity *-d- MainViewModel

abstract ViewModel
class MainViewModel
ViewModel <|-l- MainViewModel

MainViewModel *-d- AuthRepository

abstract AuthRepository
class AuthRepositoryImpl
AuthRepository <|-- AuthRepositoryImpl

AuthRepositoryImpl ..> AuthApi

```

# ViewModelの生成と取得

`ViewModelProvider.Factory`を使って、任意の引数付きのViewModelを生成・取得します。

## Factoryヘルパー関数

動画では、汎用的なヘルパー関数である`viewModelFactory`関数が紹介されています。

```kotlin:ViewModelFactoryHelper.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

fun <VM: ViewModel> viewModelFactory(initializer: () -> VM): ViewModelProvider.Factory {
    return object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return initializer() as T
        }
    }
}

```

## MainViewModelの生成と取得

`viewModel`関数とFactoryヘルパー関数とを使って、次のように`MainViewModel`インスタンスを生成・取得できます。

```kotlin
:
import androidx.lifecycle.viewmodel.compose.viewModel
import com.plcoding.manualdependencyinjection.presentation.MainViewModel
import com.plcoding.manualdependencyinjection.presentation.viewModelFactory
:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ManualDependencyInjectionTheme {
                val viewModel = viewModel<MainViewModel>(
                    factory = viewModelFactory {
                        MainViewModel(MyApp.appModule.authRepository)
                    }
                )
                :
            }
        }
    }
}
```

# まとめ

動画で紹介された`viewModelFactory`ヘルパー関数を使って、任意の引数がある`ViewModel`クラスのインスタンスを`viewModel`関数を用いて、生成・取得ができることを学びました。

**補足 - ViewModelProvider.Factoryは不要**

動画では、`ViewModelProvider.Factory`を用いていますが、現在では、その必要はありません。

>June 29, 2022
androidx.lifecycle:lifecycle-*:2.5.0 is released.

https://developer.android.com/jetpack/androidx/releases/lifecycle#2.5.0

> `lifecycle-viewmodel-compose` now offers a `viewModel()` API that takes a lambda factory for creating a `ViewModel` instance without requiring the creation of a custom `ViewModelProvider.Factory`.

```kotlin
// Within a @Composable, you can now skip writing a custom Factory
// and instead write a lambda to do the initialization of your ViewModel
val detailViewModel = viewModel {
  // This lambda is only called the first time the ViewModel is created
  // and all CreationExtras are available inside the lambda
  val savedStateHandle = createSavedStateHandle()
  DetailViewModel(savedStateHandle)
}
```

よって、MainViewModelを生成・取得するには、次のコードで実現できます。

```kotlin
val viewModel = viewModel {
    MainViewModel(MyApp.appModule.authRepository)
}
```