https://kotlinlang.org logo
#uniflow
Title
# uniflow
s

Stephan Schuster

08/10/2021, 10:19 PM
Hi! I'm an "experienced old-school Android developer" who is trying to learn and establish a new tech stack based on Kotlin, Coroutines, Flow, MVI/UDF, Jetpack Compose and related stuff. Currently, I know very little about these things. So please forgive me if I am asking too many stupid questions below. If so, just ignore me... After studying lots of articles, videos, MVI framworks and sample code, I decided to go with either orbit-mvi or uniflow-kt. Both are great frameworks and apply MVI/UDF without much boilerplate. As such, my decision cannot be too wrong. Nevertheless, I would like to (but due to lack of knowledge cannot) fully understand the pros and cons on either side. That's where I need your "objective" help. :-) In general, I feel that uniflow-kt has a simpler, cleaner and more consistent API with better naming. By intention it abstracts away more stuff. I guess for a newbie like me, this makes it easier in the beginning. But will it also pay off in the long run or will I be limited somewhen in the future once I am a bit more experienced with "the new shit"? Are there any limitations at all, e.g. when not exposing Flows? In fact, uniflow-kt relies solely on LiveData for state and events while I basically read everywhere that StateFlow and SharedFlow/Channel is the way to go for MVI. Is this true? Should this bother me? Or is it really just an implementation detail? I cannot tell at the moment. Quite similar, this comment here: https://medium.com/@josip.krnjic/i-dont-like-the-setstate-method-ead7253ced9c. Can really all mentioned downsides be handled with the uniflow-kt features listed here: https://github.com/uniflow-kt/uniflow-kt/blob/master/doc/coroutines.md? Next question as found here: https://medium.com/@tfcporciuncula/whats-the-motivation-to-have-the-io-dispatcher-as-the-default-465a48c109ff. Why was IO chosen as default dispatcher over Main even though it always entails 2 thread switches? What actually happens when my ViewModel was destroyed when returning from IO? Some more thoughts - mainly related to this nice MVI framework comparison (written by orbit-mvi): https://proandroiddev.com/top-android-mvi-libraries-in-2021-de1afe890f27 • Inheritance instead of composition is okay for me. It's surely less flexible but sufficient and a bit simpler in most cases. • The "generics" issue mentioned in the article seems to be misunderstood, do you agree? In fact, I prefer being able to use any UIState and UIEvent in my AndroidDataFlow without the need to specify a single type or even Nothing upfront. This simplifies the API, should provide more flexibility (if not misused) and also makes handling states and events in the view more consistent (both with "when"). • I cannot judge the fact that uniflow-kt runs actions in sequence via actor. Could this really lead to potential unresponsive UIs? Was running actions in parallel considered to be a bad idea? If yes, why? • Last thing: multiplatform. Of course it would be nice. But I don't need it for now and assume things would become more complex then. Is there a rough guess if and when it will be available for uniflow-kt? So many questions, I know... Sorry and thanks for reading until here. I would really appreciate if you could answer or comment on some of them. Anything else I should consider when choosing between orbit-mvi and uniflow-kt?
a

arnaud.giuliani

08/11/2021, 4:32 PM
Hello Stephan, thanks for your interesting feedback. I’ll try to give you some answers
Inheritance instead of composition is okay for me. It’s surely less flexible but sufficient and a bit simpler in most cases.
You can compose your own ViewModel if needed. The easy case is cleary to use the AndroidDataFlow class
I cannot judge the fact that uniflow-kt runs actions in sequence via actor. Could this really lead to potential unresponsive UIs? Was running actions in parallel considered to be a bad idea? If yes, why?
at the beginning of coroutines, ther actor approach was the only way to guarantee the execution of UI actions in order. We were using an actor under the hood to help sequence it.
Why dispatching on IO? this is a choice by default. We can now reconsider it as we could execute directly an action
The help of an actor is to help the modification of your state in a reproducible way. Enqueueing it in an actor is a good way to be sure we can run one action at a time, against you VM state(s)
Are there any limitations at all, e.g. when not exposing Flows? In fact, uniflow-kt relies solely on LiveData for state and events while I basically read everywhere that StateFlow and SharedFlow/Channel is the way to go for MVI. Is this true? Should this bother me? Or is it really just an implementation detail? I cannot tell at the moment.
From the Uniflow perspective, it’s a internal implementation detail. You should even don’t care about it, as you finally just get your coroutines once your open
action { }
block
The only interesting thing is that it can open a more Multiplatform design with StateFlow API
less blocked by Android platform actually
s

Stephan Schuster

08/12/2021, 8:39 PM
Hi Arnaud, thanks for your answers. So basically you're saying that not using and exposing Flow(s) entails no limitations in the long run and therefore can be considered as implementation detail. Uniflow uses an Actor to always run entire actions in sequence which ensures that they modify state in a reproducible way. I assume this is (sometimes) slower than the Orbit approach which runs transformations still in parallel and only sequences the reduce blocks working on volatile state but therefore probably cannot guarantee reproducability until all actions/intents are done. Understood correctly so far?
I didn't clearly understand your answer in regards to IO. Is it still an appropriate default? And more interestingly "What actually happens when my ViewModel was destroyed when returning from IO?"
a

arnaud.giuliani

08/24/2021, 8:09 AM
It has been a progressive things since coroutines implementation and design for Android. We could also propose to first by default on Main. But then it forces you to define where you call subcalls (on IO for background calls)
9 Views