Kotlin Multiplatform — Solving network responses with teamwork

Guilherme Delgado
ProAndroidDev
Published in
4 min readJun 25, 2021

--

Photo by Quino Al on Unsplash

One of my 2021 new year’s resolutions was to dive in into Kotlin Multiplatform (KMP). I strongly believe that KMP is the only¹ framework created for native mobile developers, because it’s build upon the philosophy of becoming a code-sharing tool. This makes it possible to share platform independent logic, while preserving the platform specific one, in other words, UI/UX best practices.

My idea was to start by creating small proof-of-concept projects, gradually incrementing their complexity while trying to reach as close as I could to my “real life” problems.

The goal of this article is not to become another comparison between cross-platform frameworks — there’re a bunch of good articles already out there and my choice is made — , my goal it’s to share how we, as a team (me & Charles Prado), solved a particular problem regarding network responses.

Teamwork

If one were to ask me what changed considerably in my daily coding life — as an Android developer — when using KMP, I would say: teamwork.

The team has to keep in mind that for now, iOS is the weakest link, in a way that all the shared code must be validated by both platforms before merging it into main.
It should not be assumed that it will work just by evaluating on Android. We should also keep in mind that there may be slight differences in architectural patterns thus, a team reassessment of the best approach to follow, should be considered. KMP came to impose teamwork, so, it is in our best interest that it flows in the best possible way for everyone.

That being said, let’s illustrate what I mean with some code.

Android & Shared modules

In one of my previous articles, I’ve shown how we could take advantage of a sealed class to better illustrate use cases when consuming data from our repositories/managers:

For androidApp and shared modules, the approach remains the same:

But does it work on iosApp just because it works on androidApp? Let’s find out.

iOS module

I won’t go into details about how Kotlin/Native (K/N) compiler works, but briefly, it will generate Objective-C code. As of today² , K/N does not interop with Swift directly, only via Objective-C so we need to check first if Objective-C knows what a sealed class is and also if it supports Generics on classes. Short answer for the second: it does.

The following code is from the shared module:

K/N will translate:

into:

note: I've removed a few not relevant fields from this method, the code maybe invalid, but that’s not the focus.

In Swift it will be accessible as:

Thus, on iosApp we would expect to consume it like:

But for some reason the Error always returns null 🤔.

Let’s investigate the translated sealed class:

We have three interfaces but what quickly caught our attention was the fact that the completionHandler(Data?, Error) is using the ReservationResult as Data instead of using ReservationResultSuccess. OK, with this in mind, we’ve tried this:

But unfortunately the compiler will not allow the ReservationResultError cast (we yet fail to understand why). Not only this invalidates the previous cast approach but it won’t allow either switch over out sealed class.

Resuming: completionHandler will use the ReservationResult interface as Data and apparently will ignore ReservationResultSuccess and ReservationResultError headers. And, as Rui commented, NSError propagation to swift via completionHandler only happens by throwing exceptions. In our case we’re wrapping the success/error into a sealed class Result, the error param in swift will always be empty and data will have to be unwrapped to see what’s inside. This is a clearly disadvantage of K/N missing Swift interoperability.

Let’s try to find a solution for this.

Solution (workaround)

Trying to solve this problem I’ve created the following abstraction:

My intention was to change the less I could on androidApp and shared modules while helping out iosApp. All the sealed class results were changed to:

iosApp calls changed to:

androidApp kept unchanged. Done.

Conclusion

This illustrates a common situation where apparently the first approach was OK, since it was working for androidApp, but in reality it was broken for iosApp. Once again, in my opinion, KMP is all about great teamwork, because only then we can overcome situations like this, while also avoiding part of the team feel disregard leaving room to become angry against the framework. Keeping this in mind, KMP has huge potential to become a top pick for future projects.

Feel free to reach me out and Charles Prado on Kotlinlang’s slack if you want to discuss this further, we would appreciate it.

I hope you find this article useful, thanks for reading.

🎉 Featured in Kotlin Weekly #256

[1]: When compared to Flutter, ReactNative and Xamarin.

[2]: Kotlin 1.5.10 | Roadmap

--

--

Software Engineer at BlissApplications 🇵🇹 • github.com/GuilhE • When I’m not coding I’m 🧗🏽or 🏄‍♂️