Hello everyone, When using `kmm-viewmodel` and lau...
# multiplatform
d
Hello everyone, When using
kmm-viewmodel
and launching a coroutine within
Copy code
private val mutableStateFlow: MutableStateFlow<String> = MutableStateFlow(viewModelScope,"")
val stateFlow = mutableStateFlow.asStateFlow()
viewModelScope.coroutineScope.launch {
     delay(1000)
     mutableStateFlow.value = "new state"
 }
In iosApp I do
@StateViewModel *var* viewModel = KMMViewModel()
and pass
stateFlow
to appropriate either swiftUI/compose multiplatform UI element Changes of this state are never reflected in the UI. Did someone experience a similar issue?
r
Hi! What’s the definition of
mutableLoginUiStateFlow
?
d
Hey, it was typo. It’s just that swiftUI doesn’t reflect changes when I update it from a launched coroutine.
r
Are you changing the StateFlow value on the main dispatcher as in your example?
d
Yes yes, I do it from
viewModel.coroutineScope
In android this is fine btw, it works, I am having issues in iOS only
r
Alright. I don't see anything strange in your example code. Could you possibly share a minimal reproducable sample?
d
shared -> common -> QrScanViewModel
Copy code
open class QrScanViewModel : KMMViewModel(), KoinComponent {

    private val _scannedCode = MutableStateFlow(viewModelScope, "Initial value")

    @NativeCoroutinesState
    val scannedCode = _scannedCode.asStateFlow()

    fun updateScannedCode(result: String) {
        _scannedCode.value = result
    }

    fun flowTest() {
        updateScannedCode("Here it does update UI.")
        viewModelScope.coroutineScope.launch {
            flow {
                delay(1000)
                emit("one")
                delay(1000)
                emit("two")
                delay(1000)
                emit("three")
            }.collectLatest {
                Logger.i { "Here it does not update UI :/ $it" }
                updateScannedCode(it)
            }
        }
    }
}
iosApp -> QrScanViewModel
Copy code
import Foundation
import shared

extension QrScanView {
    @MainActor class QrScanViewModel : shared.QrScanViewModel {
        @Published var isPresentingScanner = false
        
        override init() {
            super.init()
            self.testFlow()
        }
        
        func testFlow(){
            super.flowTest()
        }
    }
}
iosApp -> QrScanView
Copy code
struct QrScanView : View {
    
    @StateObject var viewModel = QrScanViewModel()
    
    var body : some View{
        VStack(spacing: 10){
            Text(viewModel.scannedCode)
        }
    }
}
r
Thanks! Just tested this and issue is with the
@StateObject
. It should be
@StateViewModel
. Once you change that the code works as expected. Note: “Here it does update UI.” wasn’t actually a state change. It runs synchronously when the viewmodel is created. Which basically makes it the initial value.
d
Rick, thank you a lot for such availability. Here is the conclusion on this. There where 2 cases where me and my colleague were getting this undesired behavior: • He used
@StateObject
instead of
@StateViewModel
• I used compose multiplatform that led to these issues: ◦ I passed viewModel.testFlow directly to the compose function and it didn’t perform recomposition ▪︎ Reason: well, of course
viewmodel.testFlow
is not a compose state ▪︎ Solution: added wrapper compose function having only viewModel parameter in
shared.common
. Then collect
viewModel.testFlow.collectAsState
inside that wrapper and passed it to another compose function.