This is a translation of the article @panini wrote for the Mercari Advent Calendar 2018. The original Japanese article is available here.
At Mercari, much of our Android app development is done in Kotlin.
Kotlin is mostly known for its use when running on the JVM, however the official projects KotlinJS and Kotlin/Native allow us to use Kotlin in JavaScript and Native (C) environments too!
Kotlin Multiplatform
With Kotlin Multiplatform, we are able to support multiple platforms with a single codebase.
Problem
I think that DRY code is very important.
In my opinion, the fact that we have to reimplement all our business logic on each end client that we support is not a very efficient way of working. On top of this, each time we reimplement this logic, the chances of bugs happening increases, as does the maintenance cost.
Because of the pain that this creates, projects such as ReactNative and Flutter were born – being able to support multiple native clients using a single code base sounds like a great solution, however these projects also require you to share the UI code as well, which means we are unable to take advantage of each platforms unique points, unless we are okay with littering our code with checks such as if (platform == "iOS")
.
The only part I want to be able to share is the logic, so I wrote a proof of concept for sharing logic between Android & iOS!
Architecture
Because this project is a proof of concept, I went with MVVM architecture as it is simple to implement.
The architecture in a standard app would look like this:
The most important point for choosing MVVM is the dependency goes from View → ViewModel. Because the ViewModel doesn’t need to know anything about the View, even if we move the ViewModel to a library project, there is no problem.
The planned architecture for this project should hopefully look like the image below:
Project structure
In a Kotlin multiplatform project, we are unable to use classes that are defined outside the Kotlin Standard Library. This means any of the classes that we’re used to being available from java.lang
or java.util
are not available. When we want to work with dates, we can’t use the standard java.utilDate
, and for networking we don’t have access to java.net.Socket
.
But, the Kotlin Standard Library doesn’t provide an alternative to java.net.Socket
, so what are we supposed to do? Thats where the strength of Kotlin multiplatform comes into play.
Kotlin multplatform provides the expect
and actual
keywords which work as a type of interface for multiplatform projects. If there is a class that is only available at platform compile time that we want to use, we can tell the multiplatform compiler that it is available by creating an expectation: expect class Foo
. This way, we can tell the compiler that “when this project is compiled to a specific platform, this class will be available”. For example, if we want to use the java.util.Date
class, we could create an expectation such as:
expect class Date { fun getTime(): Long }
Once we have this expectation, we must create the actual
implementation on each platform that we will support. We can either implement these classes from scratch, or map them to already existing classes:
actual class Date = java.util.Date
Result
The proof of concept app is available on Github here!
The details of the implementation will be available in a follow up blog post, however it was much easier to implement this project where both Android and iOS have their own bespoke UI, but the business logic is all backed by a single library.
Gotchas
The implementation itself wasn’t that difficult, however there were a few places that proved difficult which I will go over here.
Within the multiplatform section, you can only use full Kotlin libraries, multiplatform libraries, or write your own wrapper around platform classes
This isn’t a huge issue, however it is still present. Generally speaking we don’t use that many libraries within our business logic, however useful libraries such as RxJava are implemented – as the name implies – in Java, which means that we cannot use them in our multiplatform library. RxJava is also reasonably complicated too, so it is not feasible to write a wrapper around it either. However, there is a new project called Reaktive by Badoo which is built as a multiplatform library which could be used instead.
Network 😇
I’m an Android Engineer by day, and so while building this I initially reached for OkHttp + Retrofit for the networking side of this project, however these are also implemented in Java and so are unavailable. I initially though that this would be the killer for this project, however just as I was about to give up, Ktor released a client library as well, which supports multiplatform and so I was able to use this instead.
AndroidStudio
At the time I was using the latest AndroidStudio 3.3 build, which did not correctly support multiplatform classes. While the app would compile okay, the IDE did not know which classes to import, and so any classes imported from the multiplatform library would be underlined in red, and did not support any auto completion. I haven’t yet tested this with newer version of AndroidStudio.
Conclusion
I had a lot of fun implementing this project! We do have to create a UI per platform, which means that the overall amount of code required is more than that if it was written in ReactNative or Flutter, however I was able to create a UI that complemented each platform separately, and can also support a web UI with very little effort so overall I think this will be a good option for scaling in the future.
Also, because the library can be separate from the app and imported as a dependency, it fits a microservice architecture well.
If you have a chance, I would recommend trying it out!
We are hiring!
Mercari is currently hiring for Android positions and looking for mobile engineers with Android and Kotlin experience.
Check out the JDs below. Don’t forget to mention KotlinWeekly
in the reference section when you apply.
Software Engineer, Android (Tokyo)
https://workable.com/j/96FCB426BE
Software Engineer, Android Architect (Tokyo)
https://workable.com/j/E2C2F356B4