https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
a

Alex Anisimov

02/15/2021, 9:33 AM
Hi everyone! We know that when it comes to using Kotlin technologies the best innovations come from you, our community! Do you have any tips or tricks for building cross-platform applications with KMM? We’d love to know them! Share your greatest tips with the community by replying to this message. We will feature the best tips on our Twitter along with a shout out to the author.
👀 7
b

Benoît

02/15/2021, 11:13 AM
I figure that the best architecture to use for KMM was Functional Programming and MVI. Kotlin Native doesn't play well with mutable state and Functional Programming forces you to remove side effects and mutable state so they work really well together. MVI works well with Functional Programming, as it's easy to have a set of functions return a UI state. Functional Programming doesn't require any dependency injection framework, dependency management comes out of the box with the Reader monad. I think this combo is probably the best one to reduce the Android/iOS code to the minimum, in our codebase, Android/iOS modules contain only UI code
d

Daniele B

02/15/2021, 11:58 AM
b

Benoît

02/15/2021, 12:53 PM
@Daniele B Amazing series of article, even though my architecture is slightly different because of FP, there are plenty of things I'm gonna steal from yours 😄 Thank your for sharing your knowledge
♥️ 1
d

Daniele B

02/15/2021, 12:57 PM
Interesting, let me know which are the parts that somehow conflict with your FP approach
b

Benoît

02/15/2021, 1:06 PM
The state reducers mostly, I tend to avoid functions that return Unit, as they are a sign of side effects which I'm trying to avoid, like these
Copy code
fun StateManager.setLoading() {
   state = state.copy(isLoading = true)
}
suspend fun StateManager.setCityData(city: String) {
   val detailData = dataRepository.fetchCityData(name)
   state = state.copy(detail = detailData, isLoading = false)
}
Instead, I use "business logic" functions that look like this. The NetworkCall functions are provided by repositories, they take care of caching
m

Michal Klimczak

02/15/2021, 1:19 PM
Not sure if that counts as a tip/trick because it's become a lib, but here's my two cents regarding consuming coroutines from ios https://github.com/FutureMind/koru
👍 1
d

Daniele B

02/15/2021, 1:38 PM
@Benoît I am using a KMP AppState, which is propagated to the Declarative UI layer via StateFlow. I guess you are not using StateFlow? In my experience StateFlow fits very well to an MVI archirecture and to the Declarative UIs.
b

Benoît

02/15/2021, 1:47 PM
No I am not using StateFlow, I am using Flow as you can see on my screenshot, the View observes this Flow. The reason why I'm not using StateFlow is because I don't want the state to be "remembered", to me it's a side effect. Whenever the View gets visible, if it needs data it will request a new Flow. This way there's no shared state which is what I'm trying to avoid. I understand what you're saying about StateFlow, it fits MVI very well, but it's not very FP in my opinion. Although this might just be personal preferences 🙂
d

Daniele B

02/15/2021, 1:51 PM
Declarative UIs require a single source of truth (the AppState). So, I guess you are not dealing with DeclarativeUIs.
b

Benoît

02/15/2021, 2:03 PM
I have a single source of truth in my data layer, either the cache or the backend. But I don't have 1 single god object that contains everything, I guess that's the main difference. Each domain object has its own cache
Maybe you're right, maybe I'm not using Declarative UI, only thing I know is that I use MVI and FP :)
f

Francis Mariano

02/15/2021, 2:06 PM
Do you have a sample about your architecture (github)? @Daniele B @Benoît
d

Daniele B

02/15/2021, 2:06 PM
Declarative UIs are the new UI toolkits in Android (JetpackCompose) and iOS (SwiftUI)
b

Benoît

02/15/2021, 2:08 PM
Well I'm not using compose so I guess I'm not, you're right. I was mostly talking about the business logic to be fair, the UI just gets an intent, I don't really care how it displays it
d

Daniele B

02/15/2021, 2:10 PM
In my architecture I make a distinction between the "*AppState*" and the "*DataRepository*". The "DataRepository" holds unformatted data and deals with caching. It's agnostic to anything happening on the UI layer. The "AppState" defines exactly all fields needed by the Declarative UI layers, applying the correct display formats.
b

Benoît

02/15/2021, 2:13 PM
@Francis Mariano, read @Daniele B 's article, it's very interesting and you'll learn about their architecture. I haven't written any article about mine yet, I'll post it here if I ever do it :)
d

Daniele B

02/15/2021, 2:13 PM
@Francis Mariano I still haven't published a public repository as I am working on commercial products, but I hope to publish a sample soon.
f

Francis Mariano

02/15/2021, 2:15 PM
Yes, I already read Daniele's article. It is very good.
❤️ 1
b

Benoît

02/15/2021, 2:18 PM
@Daniele B same, repository and UI states are different objects in my architecture. So I'm guessing the main difference between our architecture is that you keep a reference to the UI state whereas I recompute it whenever I need it
d

Daniele B

02/15/2021, 2:29 PM
@Benoît yes, it looks like. In my experience it's also useful to keep a neat separation of concerns between the KMP ViewModel (where the AppState is defined) and the KMP DataLayer. Ideally, in a project, you would have a KMP developer just dealing with the ViewModel (focusing on the data to provide to the UI layer), and one KMP developer just dealing with the DataLayer (connecting to the different data sources and managing the caching mechanism).
b

Benoît

02/15/2021, 2:35 PM
I agree, the data layer should be completely UI agnostic. The presentation layer takes care of formatting the domain model into UI Intent. The "domain" is basically the backend, and the "presentation" is the front-end
2 Views