1

sorry for my bad english :(

I want to create app with KMM, and maximize shared code. And only that i want to write twice is UI and Navigation. So i create ViewModel in shared code.

open class ViewModel<T>(initialState: T) where T : ViewState {

    private val _state = MutableStateFlow(initialState)

    val currentState: T
        get() = stateFlow.value

    val stateFlow: StateFlow<T> = _state

    fun updateState(
        context: CoroutineContext = EmptyCoroutineContext,
        mapping: (currentState: T) -> T
    ) { GlobalScope.launch(context) { _state.emit(mapping(_state.value)) } }
}

Where ViewState is just

interface ViewState

that uses to implement in sealed class, that describes current view state. When it changes UI receive change and updates. State can looks like (first free public API that i found returns random dog image):

sealed class DogImageViewState : ViewState {
    object Loading : DogImageViewState()
    data class Content(val image: String) : DogImageViewState()
}

OKAY

So i create concrete ViewModel to share logic between iOS and android

class DogImageViewModel(initialState: DogImageViewState) : ViewModel<DogImageViewState>(initialState) {

    private val interactor : GetDogImageUrlInteractor by dogDi.instance()

    init {
        getImage()
    }

    fun reload() {
        updateState { DogImageViewState.Loading }
        getImage()
    }

    private fun getImage() {
        GlobalScope.launch(ioDispatcher) {
            val image = interactor.getImageUrl()
            updateState { (it as? Content)?.copy(image = image) ?: Content(image = image) }
        }
    }
}

Simple, innit? Viewmodel initializes with Loading, and immidiately starts image loading. After image has loaded it goes to new state. On refresh actions exactly the same

In Android usage is very simple, jetpack compose can collectAsState StateFlow and there nothing to do anymore, just decribe an ui with state

Problem is in iOS

We must "bridge" observable model for iOS. iOS has ObservableObject mechanism and I has extended it

class ObservableViewModel<T : ViewState>: ObservableObject {

    @Published var state : T
    
    init(viewModel: ViewModel<T>) {
        self.state = viewModel.currentState
        viewModel.onChange { newState in
            self.state = newState as! T
        }
    }
}

Looks good. Generic class for ViewState... At now i can use it with SwiftUI! Oh no...

When I try to use ObservableViewModel in code i receive an error

    @StateObject var vm = ObservableViewModel<DogImageViewState>(viewModel: DogImageViewModel(initialState: DogImageViewState.Loading()))

Type 'DogImageViewState' does not conform to protocol 'ViewState'

But it is exactly conforms!!!

When i jump to definition DogImageViewState i see next

__attribute__((swift_name("DogImageViewState")))
@interface DogDogImageViewState : DogBase <DogSharedViewState>
@end;

Looks like not conforms at all. What is the Base<ViewState> (DogBase because Dog feature builds in separate framework dog and DogSharedViewState because of ViewState declared in shared)? Why is not just the ViewState?

How to explain to swift that DogSharedViewState and exactly the same?

iamthevoid
  • 438
  • 4
  • 16

0 Answers0