Rene GensAuthorBlogGitHubLinkedIn

Sharing ViewModels in KMP

22 April, 2025

Trying to share ViewModels between Android and iOS. The challenge is that ViewModel is Android-specific, but the logic can be shared.

The solution is to create a shared state holder and platform-specific ViewModels:

// commonMain
class SharedUserState {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user.asStateFlow()
    
    suspend fun loadUser(id: String) {
        _user.value = repository.getUser(id)
    }
}

// androidMain
class UserViewModel(
    private val state: SharedUserState
) : ViewModel() {
    val user = state.user.asStateFlow()
    
    fun loadUser(id: String) {
        viewModelScope.launch {
            state.loadUser(id)
        }
    }
}

// iosMain - use directly or wrap in Swift
val state = SharedUserState()

But here's what I learned - if you're using Compose Multiplatform, you can use the state holder directly in Compose. No need for ViewModel:

@Composable
fun UserScreen(state: SharedUserState = remember { SharedUserState() }) {
    val user by state.user.collectAsState()
    // Use user
}

Also, if you need Android-specific features like SavedStateHandle, you'll need expect/actual or pass it as a parameter.

One gotcha - lifecycle management is different on each platform. Android has ViewModel lifecycle, iOS doesn't. You need to handle cleanup manually on iOS or use platform-specific lifecycle hooks.

And if you're using dependency injection, you'll need platform-specific modules for ViewModels, but the shared state can be provided from common code.

Thanks for reading! Check out more posts on the blog if you'd like.

© 2025, Built with Gatsby