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 PMStateFlow
Kurt Renzo Acosta
09/15/2020, 3:57 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 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.Kurt 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 AMStateFlow
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/flosch
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.Daniele 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.Daniele 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()
}
}
}
rocketraman
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.Main
dispatcher. See: https://github.com/Kotlin/kotlinx.coroutines/issues/470.Daniele B
10/05/2020, 3:56 PMrocketraman
10/05/2020, 3:57 PMDaniele B
10/05/2020, 3:58 PMrocketraman
10/05/2020, 3:59 PMCFlow
makes it easier to not mistakenly use that onChange
method where it shouldn't be used.Daniele B
10/05/2020, 4:28 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?Flow
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