Daniele B
09/15/2020, 3:51 PMStateFlow!!!
I can easily say 85% of the code is on Kotlin Multiplatform, and 15% only on the platform-specific declarative UI (Compose and SwiftUI)!
on Android, it uses Compose's `collectAsState`:
@Composable
fun MainLayout(coreModel: CoreViewModel) {
val appState by coreModel.stateFlow.collectAsState()
...
}
on iOS, it uses a very simple class extending ObservableObject , where a StateFlow “listener” is initialized:
class AppViewModel: ObservableObject {
let coreModel : CoreViewModel = CoreViewModel()
@Published var appState : AppState = AppState()
init() {
coreModel.onChange { newState in
self.appState = newState
}
}
}
this is the shared `ViewModel`:
class CoreViewModel {
internal val mutableStateFlow: MutableStateFlow(AppState())
val stateFlow: StateFlow<AppState>
get() = mutableStateFlow
fun onChange(provideNewState: ((AppState) -> Unit)) {
stateFlow.onEach {
provideNewState.invoke(it)
}.launchIn(CoroutineScope(Dispatchers.Main))
}
}Kurt Renzo Acosta
09/15/2020, 3:54 PMFlow on your ViewModels? If so, were you able to consume it on iOS? I'm still doing the CFlow hack and I wanted to get rid of it soonDaniele B
09/15/2020, 3:55 PMStateFlowKurt Renzo Acosta
09/15/2020, 3:57 PMKurt Renzo Acosta
09/15/2020, 4:02 PMDaniele B
09/15/2020, 5:01 PMStateFlow code on iOS is very straightforward. it’s just a “callback” providing the new state.flosch
09/15/2020, 5:28 PMKurt Renzo Acosta
09/15/2020, 5:28 PMStateFlow like we use it on Android.
I have the same pattern here with the help of KotlinConf’s CFlow so that I don’t have to create a callback for every StateFlow. I just hoped it would be possible to avoid it since suspend functions can be used in iOS but I guess not for Flow yet.Daniele B
09/15/2020, 5:31 PMDaniele B
09/15/2020, 5:32 PMflosch
09/15/2020, 5:36 PMCoreViewModel scopeDaniele B
09/15/2020, 5:39 PMCoreViewModel as an Objective-C object, as it’s defined in an Objective-C framework. Feel free to share snippets.louiscad
09/15/2020, 5:40 PMflosch
09/15/2020, 5:50 PMCFlow uses a Closable, its the same principle, it needs to be cleaned up at some point. 👍Daniele B
09/15/2020, 5:53 PMelizarov
09/16/2020, 7:57 AMDaniele B
09/16/2020, 3:37 PMflosch
09/16/2020, 3:41 PMCFlow (cancel the Job), this would be an implementation detail.audriusk
09/16/2020, 4:01 PMIn summary, as you know, on JetpackCompose there is a very neat way to collect a StateFlow:That's because Android team (from Google) has incorporated Coroutines support into JetpackCompose. I do not expect same support from Apple 😄 I think community will have to step up and create some sort of components for SwiftUI to bind
StateFlow.
Maybe it's possible to wrap it with Swift's Combine framework 🤔 (There might be some problems since SwiftCombine is Swift-only and Kotlin Native has only supports Objective-C)Daniele B
09/16/2020, 4:14 PMcollectAsState() , so that developers don’t have to manage the cleanup themselves. But I am not sure if it’s feasible. Let’s see what’s the reply.
I also think, StateFlow didn’t exist at the time the kotlinconf app was made.Daniele B
09/16/2020, 4:16 PMKurt Renzo Acosta
09/17/2020, 9:45 PMStateFlow already existed before in the form of a ConflatedBroadcastChannel.Daniele B
09/18/2020, 1:19 AMCFlow in the Kotlin documentation, but I cannot find it. Was it also renamed to something else?Kurt Renzo Acosta
09/18/2020, 1:19 AMDaniele B
09/18/2020, 1:20 AMDaniele B
09/18/2020, 6:09 PMStateFlow and `ConflatedBroadcastChannel`:
StateFlow cannot be currently closed like ConflatedBroadcastChannel and can never represent a failure
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/Daniele B
09/18/2020, 6:11 PMflosch
09/18/2020, 6:22 PMStateFlow, but if you launch it there will still be a Job that probably should be cleaned upDaniele B
10/05/2020, 2:09 PMrocketraman
10/05/2020, 2:17 PMCFlow directly from the Kotlin Conference app along with the native dispatcher from the same place:
1. https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt
2. https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/iosMain/kotlin/org/jetbrains/kotlinconf/DispatcherNative.kt
I injected the view model's CFlow adapter into the iOS ObserverObject (on Android the view model is injected directly). From there, using StateFlow on Android normally, and on iOS using `CFlow`'s watch to set the @Published vars just worked.rocketraman
10/05/2020, 2:19 PMDaniele B
10/05/2020, 2:44 PMlouiscad
10/05/2020, 2:47 PMCFlow), because they might not be able to answer the way you hope for in the timeframe you're looking for.Daniele B
10/05/2020, 2:49 PMrocketraman
10/05/2020, 2:49 PMCFlow seems to be the way to go as of now. On that, you just call close in the appropriate callbacks in Swift code. The conference app has some examples showing this.rocketraman
10/05/2020, 2:50 PMDaniele B
10/05/2020, 3:44 PMfun onChange(provideNewState: ((AppState) -> Unit)) {
stateFlow.onEach {
provideNewState(it)
}.launchIn(
CoroutineScope(Dispatchers.Main)
)
}
into this:
fun onChange(provideNewState: ((AppState) -> Unit)) : Closeable {
val job = Job()
stateFlow.onEach {
provideNewState(it)
}.launchIn(
CoroutineScope(Dispatchers.Main + job)
)
return object : Closeable {
override fun close() {
job.cancel()
}
}
}Daniele B
10/05/2020, 3:46 PMrocketraman
10/05/2020, 3:47 PMDispatchers.Main should probably be an expect/actual which points to this native dispatcher in iOS: https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/iosMain/kotlin/org/jetbrains/kotlinconf/DispatcherNative.ktDaniele B
10/05/2020, 3:48 PMDispatchers.Main the UI dispatcher also recognised on iOS?rocketraman
10/05/2020, 3:49 PMCFlow, except CFlow is nice as it wraps any Flow, so you don't have to add that onChange method directly to your view state.rocketraman
10/05/2020, 3:51 PMMain dispatcher. See: https://github.com/Kotlin/kotlinx.coroutines/issues/470.Daniele B
10/05/2020, 3:56 PMrocketraman
10/05/2020, 3:57 PMrocketraman
10/05/2020, 3:58 PMDaniele B
10/05/2020, 3:58 PMrocketraman
10/05/2020, 3:59 PMrocketraman
10/05/2020, 4:18 PMCFlow makes it easier to not mistakenly use that onChange method where it shouldn't be used.Daniele B
10/05/2020, 4:28 PMDaniele B
10/05/2020, 4:29 PMcollectAsState()rocketraman
10/05/2020, 4:29 PMlouiscad
11/13/2020, 10:52 AMCFlow from KotlinConf app (link above) or edit it to suit your needs. Is it not enough?louiscad
11/13/2020, 11:02 AMFlow type from Swift, so you can pass it to CFlow from Swift before getting the values in iOS code.Kurt Renzo Acosta
11/13/2020, 11:03 AMCFlow on Android and iOS and I’m fully sharing my VMs. No need for a wrapper on iOS.Daniele B
12/10/2020, 10:14 PM