Skip to content

Kotlin Multiplatform Mobile— Solving network responses with teamwork

Opinion Articles

Guilherme Delgado

Bliss Applications

One of my 2021 new year’s resolutions was to dive in into Kotlin Multiplatform Mobile (KMM). I strongly believe that KMM 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 KMM, 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. KMM 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:

 

Coroutines, Retrofit, and a nice way to handle Responses

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) works, but briefly, gradle 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:

 

 

In Swift it will be accessible as:

 

 

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

 

But for some reason — we still don’t understand —, the Error always returns null 🤔.

Let’s investigate the translated sealed class:

 

But for some reason — we still don’t understand —, the Error always returns null 🤔.

Let’s investigate the translated sealed class:

 

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. 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

We don’t believe this is a final solution because we have the dead weight of Error, and Data shouldn’t behave as both, but 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, KMM 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, KMM 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 on Kotlin Weekly #256 🎉

 

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

[2]: Kotlin 1.5.10 | Roadmap

Related articles

Opinion Articles
RxRepository: Building a testable, reactive, network data repository using RxSwift (part 1)

In this series we will tackle the problem of optimizing network access to fetch data from the network, a common theme of networked applications. While it is certainly trivial to fetch data from a server in any modern framework or OS, optimizing the frequency of access to the network, in order to save bandwidth, battery, user frustration, amongst other things, is complex. More so if you want to reduce code duplication, ensure testability, and leave something useful (and comprehensible) for the next engineer to use.

Opinion Articles
RxRepository: Building a testable, reactive, network data repository using RxSwift (part 2)

In part 1 of this series we started tackling a common problem of networked applications, that of optimizing resource usage and user experience, by optimizing network access. We typically do that by avoiding expensive resource usage, as in avoid making network calls. This avoidance is not more than a mere compromise on the type of resource we decide to spare. Trade a network call for memory space, by caching network responses. It also comes with constraint relaxation, as we do not need the latest version of a particular resource. We, thus, avoid a network call. Nevertheless we want that what we have cached to eventually expire, or to be able to forcefully reload a resource.