https://kotlinlang.org logo
d

Daniele B

11/18/2020, 12:52 PM
As announced last week, I am now finally publishing the article about my research in KMP and Declarating UIs, using the MVI pattern. I am presenting the D-KMP architecture, which allows 85% of shared code. I hope this can bring useful elements to promote Kotlin MultiPlatform! https://twitter.com/dbaroncellimob/status/1329044480515772420?s=09
❤️ 3
👏 5
👍 6
🔥 8
t

Thomas Myrden

11/18/2020, 12:54 PM
Looking forward to reading this
s

spand

11/18/2020, 1:01 PM
I skimmed it but didnt see any code. Have you published something that one could dive into ?
d

Daniele B

11/18/2020, 1:06 PM
@spand you find relevant code snippets in the part 3 of the article
s

spand

11/18/2020, 1:08 PM
Yeah. I knew it was a stretch but hoped for a complete sample 🙂
1
t

Thomas Myrden

11/18/2020, 1:17 PM
Great read @Daniele B! Currently working on a project that's C++, React Native, Swift, Obj-C, Kotlin & Java. This is going to be super helpful to try and start informing/selling my peers on KMM/KMP.
d

Daniele B

11/18/2020, 1:17 PM
@spand I didn't get the chance to create a sample repository, as I have been working on a full app. However, in the article you get all the relevant code you need.
👍 1
r

Robert Jaros

11/18/2020, 1:33 PM
I wonder if similar solutions are created for other languages (swift multiplatform?) ​​that may turn out better than KMP in the future?
f

Ferran

11/18/2020, 1:43 PM
@Daniele B In your diagram you expose
StateFlow
that goes from the native view model back to the different platforms. The thing is that I haven’t managed to get StateFlow and iOS working together. I assume that you have succeeded?
d

Daniele B

11/18/2020, 1:53 PM
@Robert Jaros Two considerations: 1) Kotlin is now de facto a multi-platform language, making any new feature available to 3 compilation targets: JVM, Native, JS. It's a huge effort, which is not trivial. 2) Swift is a language by Apple, which never had an interest in anything outside its ecosystem
@Ferran in part 3 you can see the relevant code. The StateFlow is defined as a property of the shared ViewModel.
f

Ferran

11/18/2020, 1:59 PM
I’ll give it a go 👍 which coroutines version are you using, 1.4.1?
d

Daniele B

11/18/2020, 2:00 PM
I am using 1.3.9-native-mt
f

Ferran

11/18/2020, 2:01 PM
ah! this is why
I thought StateFlow and SharedFlow is not available in 1.3.9-native-mt
r

Robert Jaros

11/18/2020, 2:03 PM
StateFlow was introduced in 1.3.6
d

Daniele B

11/18/2020, 2:04 PM
StateFlow is available since 1.3.6, as I mentioned in the article. SharedFlow was just released this month, and it's actually a generalized version of StateFlow. But in terms of state management for the UI layer, StateFlow is exactly what we need.
s

solidogen

11/18/2020, 2:32 PM
great read. I tried to make a project structure like this, but I have no luck here, I try to solve errors until I give up. I would love to see some minimum compiling implementation of android+js+ios+backend targets thrown together
f

Ferran

11/18/2020, 2:35 PM
I was looking at SharedFlow instead of StateFlow for the
events
that you also express in the diagram. So that in both directions we have a reactive approach. But as SharedFlow is not in 1.3.9-native-mt I cannot use it yet. A SharedFlow is needed in my scenario because you could push two
refresh
actions and as far as I know stateFlow does not react if the same state is emitted twice, but SharedFlow will (also you have control on overlfow for when the user clicks on refresh like crazy…)
s

solidogen

11/18/2020, 2:50 PM
I think stateflow is conflated by design, so if you got refresh state, then rotated screen, view would subscribe again and get obsolete refresh state. So one-time events are not designed to be dispatched in stateflows, exactly like those livedata hacks like singleliveevent etc
f

Ferran

11/18/2020, 2:51 PM
yup I agree ^
k

kqr

11/18/2020, 3:03 PM
in this architecture, how much could views differ on each platform?
d

Daniele B

11/18/2020, 3:08 PM
@Ferran I am following the MVI pattern, which is based on the concept of uni-directional data flow. This removes a lot of complexity, and makes the code very simple. StateFlow is ideal for the MVI pattern. I am not sure why you are interested in a bi-directional flow. This brings much more complexity, and I am not sure it has a lot of benefits. Do you have a special use case?
@kqr the difference is exactly the one between JetpackCompose and SwiftUI, which is very little. The concept behind is exactly the same, stateless declarative UIs. What changes in most cases is just the name of the components and how you specify the parameters. In most cases you can convert quickly by hand from one framework to the other one.
f

Ferran

11/18/2020, 3:11 PM
@Daniele B How do you send an event to the view model, do you call a suspend function, or you call a function that then calls a suspend function? Instead of that, with SharedFlow you can have unidirectional events from the UI and then collect the events inside the view model. This will decouple the action from the processing of the action and this gives you more control on the actions emitted, for example with the overflow you can control that only one action of the same type is emitted
k

kqr

11/18/2020, 3:11 PM
yeah but my question was more if I would like to have different UI on each platform for whatever reason
then I would need platform specific events if I get it right?
d

Daniele B

11/18/2020, 3:16 PM
@Ferran the UI layer is simply calling the events, which are extension functions of the ViewModel. Inside the events you can then define any asynchronous logic you want, with the advantage of being fully shared across any platform. You wouldn't be able to share any logic on the UI layer side.
@kqr the events are defined in the shared ViewModel, so both UI layers call the same event functions
@Ferran one curiosity? are you experimenting with declarative UIs or with traditional UIs ?
f

Ferran

11/18/2020, 4:33 PM
@Daniele B to be honest I”m experimenting with a clean architecture kotlin native modules app, I’m not giving much attention to the UI components just yet as I’m planning to keep them as lean as possible
non working but almost there, I need to figure out the sharedFlow and compilation issues https://github.com/zegnus/clean-architecture-kotlin-multiplatform
d

Daniele B

11/18/2020, 4:38 PM
@Ferran yes, I was suspecting that. If you enter the world of declarative UIs, then most of your doubts fall. Uni-directional data flow is the way.
f

Ferran

11/18/2020, 4:40 PM
@Daniele B yup, its still unidirectional, my sharedFlow is not exposed, is just how I want to handle the async call internally in the class
instead of calling a suspend function I emit an internal event to a private sharedFlow
and I want to do that in order to have fine control on the UI spamming events
d

Daniele B

11/18/2020, 4:44 PM
I am not sure I am following. Before you were talking about having a reactive approach in both directions. I don’t see the use case for a sharedFlow here.
f

Ferran

11/18/2020, 4:48 PM
ok so in your design, if a user clicks refresh 3 times, how many times do you refresh the data? (imagine that somehow the user manages to do that on a button)
d

Daniele B

11/18/2020, 4:56 PM
Inside the Event functions, you have the full flexibility to write any logic you want. You might add a logic of exiting the function in case there has been another call in the last 1 second.
f

Ferran

11/18/2020, 4:58 PM
yup indeed, but that’s dealing with states and in a complex app that will add lots of complexity. With an internal sharedFlow that’s handled for you out of the box, with any event
d

Daniele B

11/18/2020, 4:59 PM
You can add a flow directly inside the Event function.
f

Ferran

11/18/2020, 5:00 PM
ah yes, maybe! I’ll have a look
d

Daniele B

11/18/2020, 5:05 PM
Maybe you are not referring to a specific functionality of SharedFlow, but rather to the standard functionality of Flow. SharedFlow is about multiple subscribers with a replay cache. I still haven’t found a very interesting use case for SharedFlow yet.
f

Ferran

11/18/2020, 5:08 PM
yup, I was trying to build a very simple example. What I had in mind is to disconnect all data sources by using a reactive approach. So that one entity subscribes to it and another one submits actions. In this way everybody has the same state of it regardless of who calls it
but maybe that can be done with a stateFlow instead, I don’t know