Which is the best architecture to work with compos...
# compose-desktop
p
Which is the best architecture to work with compose-desktop? maybe MVC or MVP are better than MVVM this time?
p
That's the one million dollar 💵 question. I would say pick whatever you feel comfortable with 🙂. I personally prefer MVVM/MVI and use Coordinators for my Navigation State. I heard that coordinator app thing from an iOS colleague time ago and I've been liking it since.
p
Do you have a link to see how coordinators and navigation state work with compose-desktop?
p
I am working on a "kmp+compose study" kind of project/journey. Planning on building a library out of it. See link below. I also plan to make a couple of samples playing with decompose/ballast/voyager. Those are the 3 more advanced at the moment. Playing with the different approaches to state management, to see how they work. In this project for example I use the same Coordinator concept but rename it to Node. The name is shorter but is the same principle. https://github.com/pablichjenkov/uistate3
You might also see molecule from cash app and circuit from slack. Those are really good too but they sound to be under construction 🚧🏗️
p
thank you very much, but for the moment i'm searching just for a guide about how to implement a simple architecture using vanilla compose+kotlin
I whould specially like an architecture that makes easy to migrate between android kotlin/compose to desktop kotlin/compose-desktop
at this moment I don't know if in android it's common to use MVVM with compose, I learned compose here with compose-desktop
but if for example, MVVM is the recommended way to use compose in Android, I whould be more interested in knowing with architecture will let me migrate to a MVVM compose android application if I develop a compose-desktop app
to reuse the most code possible
p
When you say MVVC you mean MVVM? or something else? MVVM compose is big on Android, it is used the most with jetpack navigation. Although some people use it within Fragments too. I would say if you plan to reuse. Whatever code you have after your ViewModels can be reused, including the ViewModel itself. Some libraries I mentioned above use ViewModel in a similar way Android does. So it could be easy to reuse. I would say Voyager and Decompose are the closest to Android
But if I were you, I would not start the desktop app first and then migrate to Android. I would prefer building both apps at the same time. It would help you better with the abstractions for each platform. The project I posted has a kmp setup for desktop and Android but there is a plethora of KMP setups out in github, with iOS and web too.
e
I think it becomes easier when you realize the evolution MVC -> MVP -> MVVM. Each step adds flexibility. I don't think it makes any sense to use either MVC or MVP if you have a choice. Remember that these are just presentation architectures, and not app architectures, so you usually want to combine it with something like CLEAN.
With Compose I don't think you can even use MVC, and it would be heavily discouraged. It does play very nicely with MVVM, so that would be one more reason to go with that.
p
@enighma the problem here is that it seems that Compose-Desktop does not have an official guide with help or samples about how to implement MVVM. Or do you have a link where I can read how to achieve it without using third party libraries? Just with vanilla kotlin and Compose code. Thank you
e
@Pablo My point is that you don't really need one. MVVM is an architectural pattern and not tied to a particular OS. You don't need any libraries to use MVVM. Architectural patterns is about how objects relate to each other and what their role is. That being said it could be very helpful to have a sample project. I might create something small. But in short, you don't need any libraries for MVVM on any platform. You just need something that takes the role of M, VM, and V, and then connect them according to the pattern.
I think M would be the same on all platforms. VM I would probably use an actual/expect, since you want a ViewModel on Android to help retain the state. That being said you could use a composable as your VM as well, you just have to be very carful that you don't couple it with your views.
p
What is an actual/expect ?
e
It's when you write code in common that has a platform dependent implementation. Android ViewModel is tightly coupled with how Android apps work, and that is very unlike how a MacOS app works for instance.
p
Well i thought that the V whould be the classes with composables and the VM whould be a class that doesn't extend anything and simply holds the variables and works with them. The main doubt here is that in Android ViewModel extends a class which has the special feature that can survive configuration changes, it retains the variables. In this case, whould that be achieved with a simple class with remember mutablestateof variables?
e
One way around that is to create a VM Interface, and then implement it as an Android ViewModel on Android. Another solution which is what I think you're touching upon, is to have the ViewModel modeled as a composable, since Compose itself has a way to handle configuration changes. I wouldn't say that V is classes with composables, because not all composables are views, but all views is a composable (if you use compose). It can take a little getting used to. Compose just is so flexible that it can handle different pieces of your app. I think there's a square library that only uses it to VM or Business logic state for instance.
p
Ummm, so if the classes with the composable views of my app are not the V, what should be?
e
I can throw together a skeleton this weekend. This is an area I find really interesting, so I'd love to help out.
p
Thank you, that whould be nice
e
Yeah, so I split my composable between files. The class concept doesn't really translate to compose since they are functions.
p
Initially I was thinking that this whould work as a ViewModel for a Compose-Desktop app:
Copy code
class MainViewModel {
    var region by remember { mutableStateOf("") }
    
    init {
        region = "Europe"
    }
    
    fun changeRegion(newRegion: String) {
        region = newRegion
    }
}
Do you think it's correct? as I understand using remember state variables should do the job similar to the Android ViewModel feactures
e
I don't think you can use remember in a regular class. It only works in a Compose context.
p
ummm
now I understand why you are asking for using a Composable as the VM
if I use a simple var, can it be considered also a VM even if it is not keeping the value when the app is destroyed? Well, I think that is more common in Android and maybe using simpe var should do the job for implementing a vanilla MVVM
I mean, this:
Copy code
class MainViewModel {
    var region: String
    
    init {
        region = "Europe"
    }
    
    fun changeRegion(newRegion: String) {
        region = newRegion
    }
}
Whad do you think?
e
Copy code
class UiState(val region: String)
@Composable
fun MyViewModel(): MutableState<UiState> {
    var region by remember { mutableStateOf("") }
    
    var uiState = remember(region) {
        mutableStateOf(UiState(region))
    }

    return uiState
}
You still need to bind that ViewModel and create views.
Updated sample with some bugfixes 😄
p
whould work creating a variable in the .kt file with view composables and using them in the composables?
Copy code
var vm: MainViewModel = MainViewModel()
Or how will the binding be achieved?
e
Screens are what I consider MVVM binders if you read the official doc. That would translate to something like this:
Copy code
@Composable
fun MyScreen() {
    val viewModel by MyViewModel()

    MyContent(viewModel.region)
}
The VM usually depend on various data sources, and you need to either inject those in the VM, or you lift it out of the Screen, and pass the VM as a parameter, and connect the VM with its data sources (repositories) in your
App {}
composable instead.
p
I think this is getting a little too much complex for me
I'm really doing my first steps on Compose, and even on Kotlin
e
Yeah, I think me creating a sample could be a good start. You also do not need to use any architectural pattern when you're starting to learn something new. When you comfortable with Kotlin and Compose, you don't have the cognitive load of that, when trying to figure out how MVVM works with it.
p
I'm starting to get comfortable, I already finished my first Compose-Desktop app and I already did some android courses with kotlin in the past months
but whould be very nice to see your sample, please, tell me when you have it
e
Definitely! I'll make sure to make it simple so the concept is clear.
p
thank you very much 🙂
p
Cool fellows! You can share your App if you don't mind Pablo. Unless there is some proprietary code or so 🙂
p
oh, it is not in a repo for now, first I'll try to improve it with MVVM
it's a simple app for organizing game roms, for deleting repeated ones, that kind of job
p
Ah I see, cool buddy
p
well, going to sleep, thank you for all, good night
m
I’d just like to throw in my two €ents too. I always try to keep my view models free from any compose concepts and code so that I can also use them in a non-compose context. Therefore I only use Kotlins StateFlows in the VMs which then can be easily used by the composable functions in my views via
collectAsState()
. That way I was able to even combine them with a GUI written in JavaFX.
p
@Michael Paus do you have a simple skeleton class about how to generate a View Model and how to bind it with the composable views? For example, can you correct this demo and add something to show your proposal?
Copy code
class MainViewModel {
    var region: String
    
    init {
        region = "Europe"
    }
    
    fun changeRegion(newRegion: String) {
        region = newRegion
    }
}
And in a different .kt file:
Copy code
var mainViewModel by remember { mutableStateOf(MainViewModel()) }

@Composable
fun Window1() {
    MaterialTheme {
        Column() {
            OutlinedTextField(label = { Text("Region Window 1") }, value = mainViewModel.region, 
                onValueChange = { mainViewModel.region = it })
            Text(text = mainViewModel.region)
        }
    }
}

@Composable
fun Window2() {
    MaterialTheme {
        Column() {
            OutlinedTextField(label = { Text("Region Windows 2") }, value = mainViewModel.region, 
                onValueChange = { mainViewModel.region = it })
            Text(text = mainViewModel.region)
        }
    }
}
I'm not sure where to place the "var mainViewModel" so I placed outside both composable functions. I suposse that as it is "by remember" it will work in both composables but... please, can you improve and correct this sample? Thank you
Well, I noticed that composable functions can't be called outside a composable item, so can't do "by remember ..." in that place... and if I remove it and put simply "var mainViewModel = MainViewModel()" it doesn't work because as it is not a remmeber state variable, it is always the same for the composables.
a
Consider also not using any architecture. I’ve been just embedding the UI logic in the UI.
1169 Views