Also sharing another slide, comparing Imperative a...
# multiplatform
d
Also sharing another slide, comparing Imperative and Declarative UI, and showing why KMP shines with Declarative UIs
2
g
You can have the "declarative" code snippet into imperative as well. I'm failing to see why kmp shines with declarative UIs from the slide
j
this has nothing to do with UI toolkits. You are free to use the Flow-based state management with the legacy view system or Compose and doing so was already a best practice regardless of toolkit.
⬆️ 1
d
What I am trying to highlight here is that in the Imperative UI toolkit there is an implicit coupling, as there is the need to link any data to the specific UI component. For doing that, the “ViewModel” needs to be aware of the component ID. (I am using a broad definition of “ViewModel” here, not necessarily referring to the Android’s ViewModel component, but to any code layer in an Android/iOS application which is connecting the data to the UI components.) In a Declarative UI toolkit, using the MVI pattern, the ViewModel doesn’t need to be aware of any UI component. What it does is just to pass the data (the app state) to the UI layer, and the UI components can freely use what they need. For this reason, the full “ViewModel” can be shared across platform (here is where KMP shines) when dealing with a Declarative UI toolkit.
j
And I believe that to be incorrect. For years I have been building apps with the architecture outlined on the bottom but with a green box that used Android views instead of Compose. Nothing about Compose enables this.
d
In the Declarative UIs, components don’t have IDs that need to be referenced by the “ViewModel”. That alone increases decoupling w.r.t. Imperative UIs. Surely MVI/unidirectional data flow also worked with Imperative UIs. But now, we can take it further. The platform-specific UI layer can be incredibly tiny.
j
I don't have IDs in my presenters
Your slide compares architecture that has nothing to do with which UI system you are using
d
I think I get what you are saying. I am showing an MVI pattern just for the Declarative UI toolkit, and I could have also shown it for the Imperative UI toolkit. Maybe that’s not exactly correct. But I didn’t mean to focus on the architecture, but on the degree of decoupling that can be achieved with Declarative UIs, compared to the Imperative UIs. I will try to find a “fairer” way to describe that.
j
I haven't seen any change in coupling, for what it's worth. The change is that you write declaratively and the compiler translates it to imperative rather than you writing the imperative. That's it.
👍 1
d
I suspect it might also have to do with my definition of “ViewModel”, which is “any code layer in an Android/iOS application which is connecting the data to the UI components.” You are considering that layer as part of the “UI layer”, while I consider that part of the “ViewModel layer”.
In this article: Leland Richardson seems to consider “findViewByID” as part of the ViewModel: The view model provides data to the layout. It turns out there can be a lot of dependencies hidden here: a lot of coupling between the view model and the layout. One of the more familiar ways that you can see this manifest is through APIs that require some amount of knowledge of the shape and content of the XML layout itself, such as findViewByID.
j
i don't think it has anything to do with your definition of view model. you can take your diagram under "Declarative UI toolkit" and replace a green box with the Android view system. you've always been able to do this. declarative UI has nothing to do with it.
d
You are certainly right when you say the same pattern (MVI with StateFlow) could be applied to an Imperative UI, if you consider “findViewByID” as part of the UI layer. That slide is not particularly great in explaining the point I want to make. I will try to find a better way to explain the “coupling” difference between Declarative and Imperative UIs. Thanks for your feedback 👍
r
I think Leland's point is about coupling within the view layer. Having a multi-platform shared view model necessarily moves the view model out of the view layer, thus removing any related coupling. However, that result is achieved whether or not the UI uses Compose or XML. I think your point is that Compose may further reduce coupling between the view and the view model in the view layer but that has little to nothing to do with KMP -- its just nicer view code.
I think also your point may be that you can then use the same Compose code in other places, like desktop and web, because its not coupled to the Android view system any longer.
d
In a Declarative UI toolkit, using an MVI pattern with StateFlow, there is nothing as such as a “view model in the view layer”.
r
You have to reference it, whether tha's via
collectAsState
in Compose or collecting on the StateFlow in an imperative UI doesn't change much.
d
I am not sure I am following what you mean
r
In that case don't worry about it... I thought I was clarifying the situation but clearly not.
d
probably I should just add a slide where I write the definition of a ViewModel, and then everyone is happy 🙂
r
Well like jw said, I don't think it has anything to do with the definition...
d
if you say there is a “viewmodel layer” and a “viewmodel in the view layer”, that’s confusing
I believe a ViewModel is anything non-layout that provides data to the layout
r
Nobody said the view model is in the view layer.
The view references the view model, that doesn't mean the view model is in the view.
d
Compose may further reduce coupling between the view and the view model in the view layer
I guess I was interpreting this as “view” and “view model in the view layer” coupling. While you probably meant “view” and “view model” coupling (on the view side)
r
That statement doesn't say the view model is in the view layer. It is talking about the coupling between the view and the model, in the view layer code. That is to say, wherever the view references the view model.
It came directly after this statement, which I guess you missed:
Having a multi-platform shared view model necessarily moves the view model out of the view layer, thus removing any related coupling. However, that result is achieved whether or not the UI uses Compose or XML.
j
yeah my presenters and my UI models are all in shared multiplatform code. i can shove the Android view system in front of it or Compose, or, in this case, I also bind to Chrome's omnibox as my UI
⬆️ 1
d
@rocketraman If we assume, we are using the same architecture (MVI with StateFlow), and you are considering “findViewByID” as part of the View layer (and not of the ViewModel). Then: using Compose or anything else doesn’t make any difference in terms of shared (multiplatform) ViewModel.
r
Exactly: I believe that was JWs point, which I completely agree with. My 2 cents were to clarify what I thought your position was, which is that using Compose to get rid of
findViewById
and friends allows you to use the same view code on non-Android platforms as well. I.e. KMP gets you part of multi-platform from one direction (business domain and ViewModel), and Compose gets you part of multi-platform from another (View). If that isn't part of your thesis, then just ignore me! 🙂
d
I also think there was a confusion between two different types of coupling: • coupling between the ViewModel and the View, which is successfully overcome by the MVI pattern (unidirectional data flow), both on Declarative and Imperative UI toolkits • coupling between “findViewByID” and the XML Layout, just applying to the Imperative UI toolkit (there isn’t such a thing in the Declarative UI toolkits)
For this reason the definition of “ViewModel” makes a difference If you define “findViewByID” as part of the View layer, then JW is right by saying that there isn’t any difference whatsoever in terms of ViewModel coupling on the ImperativeUI and the Declarative UIs. Instead, if you define “findViewByID” as part of the ViewModel, clearly there is a coupling difference.
r
Maybe so, but its trivial to remove that coupling, so its not even worth talking about (at least in an article about KMP and Compose).
If I do in my UI layer (in pseudo-code): * collect on view model state * findViewById * update view I don't consider that a coupling between my view model which contains my state and my view. That code actually decouples them, not couples them. Then compose just moves that decoupling into the compiler.
If you decide to arbitrarily define a View Model as including the first two bullet points, I think you're just confusing everyone (including yourself).
d
I am just using the ViewModel definition I found on Leland’s article, which makes very much sense to me. I have never thought the Android’s ViewModel component is the real definition of ViewModel, especially as we are going into a multi-platform space.
However in a KMP/DeclarativeUIs context, I expect there won’t be anymore confusion about what a ViewModel is. The ViewModel will simply be the shared part. Very clear 🙂
r
When I say "view model" I don't mean Androids ViewModel. I mean the thing itself, not its representation on Android. I originally wrote "ViewModel" above a few times and that probably caused some confusion. Leland's article says very little about what a "view model" is, other than "something which provides data to the layout". I think that's a good definition, but I don't think you can define the view model to include any code that calls
findViewById
just based on that -- nothing about
findViewById
is inherently about "providing data" at all. Doing so seems completely arbitrary and I don't think very many people will understand it to mean that.
a
This confuses me as well. I always consider view ids as part of the view (UI) layer.
d
I think when Leland talks about coupling between the ViewModel and the Layout, is not talking about the kind of coupling which can be overcome by MVI / unidirectional flow data. He is talking about a different kind of coupling, which is the one between the “ViewModel” (which is the part of code that on Android uses “findViewById” and on iOS defines the @IBOutlet references), and the “Layout” (on Android the XML, on iOS the InterfaceBuilder).
In my slides wanted to describe that kind of coupling, but clearly I haven’t been very successful. Using the MVI pattern just on the Declarative UI toolkit evidently created some confusion.
a
Yeah. The confusing part here is:
which is the one between the “ViewModel” (which is the part of code that on Android uses “findViewById”
In Android, ViewModels do not use
findViewById
AFAIK, View subscribes to ViewModel, and again View uses
findViewById
d
Yes, I understand the confusion. Maybe I should find a way to describe this type of coupling, without mentioning the term “ViewModel”.
As it’s clear that most people don’t consider
findViewById
as part of ViewModel.
But luckily this confusion is just related to the Imperative UI toolkit. In the upcoming DeclarativeUIs, there won’t be any space for misinterpretation.
I have modified the 2 slides. This is more adherent to what I wanted to say
j
My code looks like the second image but I use Android views
d
I guess you have the extra layer of code (described in the first image), which binds the data to the UI layout. In these pictures I am showing what the UI Layout directly connects to. In the first case, it connects to the extra layer of code, which is the middle layer between what you call ViewModel and the UI Layout. In the second case, that middle layer of code is not needed: the are no component IDs to bind to anything.
j
That extra code also exists in Compose–it's the component declarations
d
I guess you are using ViewBinding, but you still have the coupling between the XML Layout and the ViewModel (or whatever you call it). There is definitely an extra layer there, which is foreign to the XML Layout (while being platform-specific), but needs to couple to it. That coupling is an extra place in your code, which you need to add/modify, whenever you want to make changes. In Compose, with MVI/StateFlow, you don’t have any extra coupling. You just receive the data and use it.
j
It's not coupling. It simply is the rendering layer. Whether I build views in code or XML and whether I use findViewById or view binding.
d
I find it hard to follow you here. I am talking about the kind of coupling (that’s the term used), Leland talks about in his article “Understanding Jetpack Compose”, which you don’t have in Declarative UIs.
j
Right but we already established that specific architectural change has nothing to do with Compose itself
j
IMO I think Leland talks about "coupling" in terms of how you are modeling the ViewModel. But it is not a general rule. The same ViewModel I use (and using ViewBinding or findViewById) can be used directly with Compose with zero changes (just an expect/actual for adding lifecycle APIs)
d
we are not talking about the architectural coupling between ViewModel and UI Layer addressed by the MVI/unidirectional data flow. We are talking about a different type of coupling here.
j
Then either a declarative UI has the same coupling as an imperative one or you are threading an incredibly nuanced needle in your definition that doesn't actually matter in practice
When I port code from view binding to compose the only thing that changes is that I no longer write how to apply the UI model's changes. That is a nice win, but I have not reduced any coupling. I still have to bind every model property to an associated UI node property in both systems.
j
I can't see where you can couple the ViewModel with an xml layout if it is not about modeling. Why I can use my ViewModel with no problems with Xml, Compose or even SwiftUI if it is coupled to an xml which even doesn't belong to the same ViewModel Gradle module and it is not implemented by this one?
d
Android’s ViewBinding is trying to fix the “coupling issues” of the Imperative UIs. On iOS, InterfaceBuilder cannot benefit of something like ViewBinding, and the “extra layer of boilerplate code” is fully revealed. In Declarative UIs, there are not component IDs and the need of any binding. The UI Layout can apply the data directly.
j
There is no difference between ViewBinding and findViewById about how to use them with ViewModels.
j
Yeah. At least coupling-wise. I'm fully in agreement there's an associated verbosity.
j
ViewModel doesn't know how the UI is created, if it is using findViewById, ViewBinding, Databinding, Compose, or whatever is irrelevant to the ViewModel, the dependency is ViewModel -> UI not viceversa. Or seeing and old and friendly picture, UI is the blue layer, and ViewModel is the green layer. Green doesn't know blue.
d
@Javier you are talking about the architectural decoupling between the ViewModel and the UI Layer. Once we agreed that “findViewByID” is not part of the ViewModel but part of the UI Layer, we all could fully agree with that. It was not the point of our discussion. We were focusing instead on the coupling within with the UI Layer, between layer of code using “findViewByID” and the XML Layout. On Android, ViewBinding is removing most of such boilerplate code (verbosity or coupling, any way you want to call it), but the same doesn’t happen on iOS InterfaceBuilder, where you need to connect each single UI component from the Storyboard to the ViewController code. With Declarative UIs, you don’t need such kind of binding, as UI components don’t even have IDs.
j
I am just talking about the slide you posted. I have seen you removed the ViewModel in the last one. BTW you can create layouts programmatically without XML too.
d
Yes, I removed the ViewModel in the updated slide, because it was not the point of discussion. It was confusing people on the architectural aspect. Yes, you can create Imperative layouts programmatically, but that’s surely not recommended.
e
@Daniele B You can have any UI toolkit with declarative UI programming… I use KMM and share the flows (state machines) and I use at the UI layer Views/XMLs on Android and ViewControllers on iOS. For sure, there is a smart layer in the middle that allows you to do that. With the diagram you shared, it looks like that you can only share the “presentation” layer when you are using Swift UI, which is not very accurate. Swift UI is only iOS 13+ and from the diagram it looks like it’s impossible to share the presentation layer unless you are using SwiftUI. We use UIViewControllers on iOS and Views on Android and we share the presentation layer. SwiftUI and Compose just make the API simpler, and, we don’t need to maintain our own bridging layer in the middle.
d
@Elka yes, the difference is that with the SwiftUI and Compose you don't have such middle layer at all
👍 1
a
@Elka We only do SwiftUI ... and we are comfortable with this as we see it not a big issue going forward ... We love all our customers but the ones with the latest version are usually the paying ones ... iOS14 is already at 80%
👍 1
d
@Elka now that Apple has extended the OS support to devices from 4 to 5 years, I am even considering to target iOS15 in case SwiftUI 3.0 brings useful new features/components. Otherwise iOS14, which is supported by all devices with iOS13.
👍🏽 2