FlowとStateFlowとstateIn()

# Flow (-> MutableStateFlow) -> StateFlow
UseCaseでFlowを作って、ViewModelでStateFlowにして、Screenで表示する方法を学習しようと思います。

結論は、「UseCaseのFlowをそのままstateIn()で流すのはよろしくない。」でした。

## 改造前のソース
APIリポジトリ
```kotlin
fun getAreaFlow(): Flow<Future<AreaApiModel>> {
    return flow<Future<AreaApiModel>> {
        val response = forecastApi.getArea()
        if (response.isSuccessful) {
            emit(Future.Success(value = response.body()!!))
        } else {
            throw HttpException(response)
        }
    }.catch { cause ->
        emit(Future.Error(cause))
    }.onStart {
        emit(Future.Proceeding)
    }.flowOn(dispatchers)
}
```
<details><summary>独自定義クラス（FutureとforecastApi）の説明はこちら</summary>

forecastApi.getArea()は、RetrofitのInterfaceです。
```kotlin
interface ForecastApi {
    @GET("common/const/area.json")
    suspend fun getArea(): Response<AreaApiModel>
}
```
Futureは、API呼び出しの状態を保持しているモデルです。
```kotlin
sealed class Future<out T> {
    object Idle : Future<Nothing>()
    object Proceeding : Future<Nothing>()
    data class Success<out T>(val value: T) : Future<T>()
    data class Error(val error: Throwable) : Future<Nothing>()
}
```
</details>

APIリポジトリを呼び出しているUseCase
```kotlin
class GetAreaUseCaseImpl @Inject constructor(
    private val forecastRepository: ForecastRepository,
) : GetAreaUseCase {
    override fun invoke(): Flow<Future<Area>> {
        return forecastRepository.getArea().map { apiModelFuture ->
            when (apiModelFuture) {
                is Future.Error -> Future.Error(apiModelFuture.error)
                is Future.Success -> Future.Success(AreaAdapter.adaptFromApi(apiModelFuture.value))
                is Future.Proceeding -> Future.Idle
                is Future.Idle -> Future.Idle
            }
        }
    }
}
```

UseCaseを使用しているViewModel
```
    private var _areaFutureState: MutableStateFlow<Future<Area>> = MutableStateFlow(Future.Idle)
    val areaFutureStateFlow: StateFlow<Future<Area>> = _areaFutureState.asStateFlow()
:
    init {
        refreshArea()
    }

    fun refreshArea() {
        _areaFutureState.value = Future.Proceeding
        
        viewModelScope.launch {
            getAreaUseCase().collect {
                _areaFutureState.value = it
                if (it is Future.Success) {
                    _center.value = it.value.centers.firstOrNull()
                    _office.value = _center.value?.offices?.firstOrNull()
                }
            }
        }
    }
```
ViewModelを使っているScreen
```kotlin
val areaState = viewModel.areaFutureStateFlow.collectAsState()
```

## 改造
FlowをStateFlowとしてそのまま使うだけなら以下でいいんですよね。
```diff_kotlin:ViewModel
-    private var _areaFutureState: MutableStateFlow<Future<Area>> = MutableStateFlow(Future.Idle)
-    val areaFutureStateFlow: StateFlow<Future<Area>> = _areaFutureState.asStateFlow()
+    val areaFutureStateFlow: StateFlow<Future<Area>> = getAreaUseCase()
+       .stateIn(
+           scope = viewModelScope,
+           started = SharingStarted.Lazily,
+           initialValue = Future.Idle
+       )

-    init {
-        refreshArea()
-    }

-    fun refreshArea() {
-        _areaFutureState.value = Future.Proceeding
-        
-        viewModelScope.launch {
-            getAreaUseCase().collect {
-                _areaFutureState.value = it
-                if (it is Future.Success) {
-                    _center.value = it.value.centers.firstOrNull()
-                    _office.value = _center.value?.offices?.firstOrNull()
-                }
-            }
-       }
-    }
```

ここで、3つの疑問が浮上しました。
1. データ取得ができたときに、_center.valueと_office.valueに初期値を入れていたがこの処理をどうする？
1. これ、UseCase側が初期値を欲したときにどうするんだ？
1. 再取得処理ってどうやるんだ？

## また改造
初期値を入れてみようということで、ぱぱっと力業？でやるとこうなるかなというのを書いてみました。
```diff_kotlin:ViewModel
    val areaFutureStateFlow: StateFlow<Future<Area>> = getAreaUseCase()
+        .map { areaFuture ->
+            if (areaFuture is Future.Success) {
+                _center.value = areaFuture.value.centers.firstOrNull()
+                _office.value = _center.value?.offices?.firstOrNull()
+            }
+            areaFuture
+        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
            initialValue = Future.Idle
        )
```
mapで入れちゃおう作戦です。とりあえず動きますが、力業感が否めない。。。
他のStateの状態に依存して、自分の状態を変える的な何かが絶対あるはずと信じて探してみます。
...う〜ん...ない！（誰か教えてください　(･_･)(._.)ペコッ）

一番多かったのが、uiStateを管理するモデルを作って、不整合を起こりにくくした形にしてmap{}で変換しようというものでした。

まずは2つモデルを作ります。
```kotlin
/** Screenで使用するモデル */
data class ForecastUiState(
    val areaFuture: Future<Area> = Future.Idle,
    val center: Center? = null,
    val office: Office? = null,
)

/** ViewModelで値を変えていくモデル */
class ForecastViewModelState(
    val areaFuture: Future<Area> = Future.Idle,
    val center: Center? = null,
    val office: Office? = null,
) {
    fun toUiState(): ForecastUiState {
        when (areaFuture) {
            is Future.Idle,
            is Future.Proceeding,
            is Future.Error -> {
                return ForecastUiState(
                    areaFuture = areaFuture,
                    center = null,
                    office = null,
                )
            }

            is Future.Success -> {
                val center = this.center ?: areaFuture.value.centers.firstOrNull()

                return ForecastUiState(
                    areaFuture = areaFuture,
                    center = center,
                    office = this.office ?: center?.offices?.firstOrNull()
                )
            }
        }
    }
}
```

```diff_kotlin:ViewModel
+    private val _areaFutureStateFlow: MutableStateFlow<ForecastViewModelState> = MutableStateFlow(ForecastViewModelState())

-    val areaFutureStateFlow: StateFlow<Future<Area>> = getAreaUseCase()
-        .map { areaFuture ->
-            if (areaFuture is Future.Success) {
-                _center.value = areaFuture.value.centers.firstOrNull()
-                _office.value = _center.value?.offices?.firstOrNull()
-            }
-            areaFuture
-        }
+    val areaFutureStateFlow: StateFlow<ForecastUiState> = _areaFutureStateFlow
+       .map { it.toUiState() }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
-            initialValue = Future.Idle
+            initialValue = _areaFutureStateFlow.value.toUiState(),
        )

+    /** init復活 */
+    init {
+        refreshArea()
+    }

+    /** refreshAreaも復活 */
+    fun refreshArea() {
+        _areaFutureStateFlow.value = ForecastViewModelState(areaFuture = Future.Proceeding)
+
+        viewModelScope.launch {
+            getAreaUseCase().collect {
+                _areaFutureStateFlow.value = ForecastViewModelState(areaFuture = it)
+            }
+        }
+    }
```

uiStateというモデルが増えただけで、結局改造前に戻りました。
この作りだと、
1. これ、UseCase側が初期値を欲したときにどうするんだ？
1. 再取得処理ってどうやるんだ？
という他の疑問も解決です。

やっぱりUseCaseのFlowをそのままScreenに出すのは現実的じゃないのかな。
誰か教えてください　(･_･)(._.)ペコッ

## 完成ソース
完成ソースは[こちら](https://github.com/jozuko/WeatherForecast/commit/0c207d19954a7d7f6c4018b5d3b578af5824c8dd)です。