はじめに
Androidモバイル開発ではJetpack Composeが主流になってきており、プレビュー機能(ホットリロード)を活用するとUI開発が効率化されます。しかし、複数のviewでプレビューが表示されないエラーに遭遇することがあります。
結論:ViewModelの依存先をmockにせよ
ViewModelやusecaseがネットワーク通信層またはデータベース層と接続しており、DIを使用する場合にこのエラーが発生することがほとんどです。プレビュー機能が依存関係を解決できないために発生するため、依存先をmockなどで置き換えることで解決できます。
概要
MVVMアーキテクチャを採用するプロダクトで、ViewModelがネットワーク層やローカルデータベース(Room等)をDIで注入している場合、プレビューが表示されません。これはDIライブラリが現在のプレビュー機能に対応していないために起こります。HiltやKoinなどのDIツールを使用している場合、repositoryなどを"モック化"することで解決できます。
解決方法
具体例(前提)
- Koinライブラリを使用したDI
- MVVMアーキテクチャ
- UseCase層なし
プレビューが動かない状況
@Composable
fun NewsView(private val viewModel:HogeViewModel) {
// UI viewModelを使った値を使用
....
}
@Preview
@Composable
fun NewsViewPreview() {
val viewModel = getViewMolde()
MyAppTheme {
NewsView(viewModel)
}
}class NewsViewModel(
private var repository: NewsRspository
) : ViewModel() {
....
}
class NewsRepository() {
suspend fun fetch() {
// APIを使ってfetchする処理など....
}
}改善ステップ
1. Repositoryのインターフェースを作成
interface INewsRepository() {
suspend fun fetch()
}2. インターフェースに準拠させる
class NewsRepository(): INewsRepository {
override suspend fun fetch() {
// APIを使ってfetchする処理など....
}
}
class NewsViewModel(
private var repository: INewsRspository,
) : ViewModel() {
....
}3. インターフェースに対してDIを修正
internal val appModule = module {
single<INewsRepository> { NewsRepository() }
viewModel { NewsViewModel(get()) }
}4. Mockのrepositoryを作成してプレビューで使用
class FakeNewsRepository(): INewsRepository {
override suspend fun fetch() {
// ネットワーク通信しない処理にする
}
}
@Preview
@Composable
fun NewsViewPreview() {
val repository = FakeNewsRepository()
val viewModel = NewsViewModel(repository)
MyAppTheme {
NewsView(viewModel)
}
}補足:アーキテクチャの改善
インターフェースを作成することで依存性を逆転させます。これにより、ビジネスロジックを含む層がrepositoryの具体実装に依存しないようになり、責務が明確になり、テスト作成が容易になります。
まとめ
Jetpack Composeのプレビュー機能は開発効率に大きく影響するため、エラーが発生した場合は放置すべきではありません。動かない時は、プロジェクト全体の依存性やアーキテクチャ見直しが必要な場合が多いため、積極的にリファクタリングを進めることが重要です。
