API 호출시점부터 View 에 보이기까지의 일련의 flow 를 작성.
예시는 Detail Screen 으로 들겠습니다.
DetailScreen 의 Compostion 에 들어오게 되면
detailUiState 변수는 viewModel 에 존재하는 detailUiState 라는 StateFlow 를 State 로서 collect 합니다.
→ viewModel 의 detailUiState 가 emit 을 한다면 DetailScreen 의 detailUiState 의 값이 변경되고 State 이기에
re-composition 을 유발합니다.
→ detailUiState 의 값이 Loading 인지, Success 인지, Error 인지에 따라 다른 Composable 을 호출해서 화면을 구성합니다.
그러면 viewModel 의 detailUiState 는 어떻게 생겼을까?
retry 는 새로고침을 위함입니다.
만약 Network 통신에 문제가 생겨 Error 를 뱉어냈다면 retry 값을 변경함으로 flatMapLatest 를 통해 detailUiState 값을 갱신시킵니다.
여기서 주요 깊게 봐야 할 부분은 getUiStateFlow 함수입니다.
결국 해당 함수(getUiStateFlow)가 detailUiState 에게 네트워크 통신의 결과를 가져다줍니다.
getUiStateFlow 는 파라미터로 들어온 block 함수를 실행시켜 UiState 로 변형한 후 emit 합니다.
block 함수(business logic) 는 kotlin.Result 자료형을 반환합니다.
- Result 가 성공이라면 UiState.Success 에 데이터 값을 넣어서 emit
- Result 가 실패라면 UiState.Error 에 Throwable 값을 넣어서 emit
- collect 되기 전에는 UiState.Loading 을 emit
그럼 DetailViewmodel 에서의 block 은
movieRepository.getMovieInfo(movieId) 입니다.
변경(2024.02.20)
위에서 getUiStateFlow 의 구현체를 보면 항상 Flow 를 반환합니다.
Flow 는 데이터의 흐름에서 통로 역할을 해주는데
Api 요청마다 일회성으로 Flow 를 생성한다는 것은 Flow 를 사용하는 의미가 없습니다.
또한 새로 고침을 할 때마다 Flow 를 생성하고 계속해서 의미 없는 collect 를 하고 있고 있습니다.
추가로, getMovieInfo 호출을 함수로 두지 않고 flatMapLatest 내부 람다 식에 넣어두니, 영화 정보를 가져와야 할 때 retry 란 변수 사용이 불가피했습니다.
위와 같은 이유로 아래로 변경했습니다.
Flow 사용 코드를 제거했으며,
repository 에 geMovieInfo 함수를 호출한 뒤 stateFlow 로 선언한 detailUiState 의 값을 변경하는 방식을 취했습니다.
이로 인해,
- 더 이상 의미 없는 Flow 를 생성 및 Collect 를 하지 않습니다.
- getMovieInfo() 를 호출하기 위해 retry 변수를 바꾸는 방식을 더 이상 하지 않아도 됩니다.
연속적으로 호출하는 MovieRepository -> MovieRemoteDataSource -> MovieService 로 타고 들어가면
최종적으로 위 함수(getMovieInfo)에 도착하는데 Result 를 반환합니다.
Retrofit 을 사용해서 호출한 service method 는 기본적으로 Call 자료형을 반환합니다.
하지만 우리가 필요한 건 Result 이기에
Custom 으로 구현한 CallAdapterFactory 를 Retrofit Builder 에 추가시켜 줍니다.
'Movie' 카테고리의 다른 글
Movie 프로젝트 edge-to-edge (0) | 2023.07.16 |
---|---|
Movie 프로젝트 Pagination (0) | 2023.07.15 |
Movie 프로젝트 구조 (0) | 2023.07.15 |
Movie 프로젝트 디자인 (0) | 2023.07.14 |
Movie 프로젝트 시작점 (0) | 2023.07.13 |