Mobile reusability in Taager

Marios Harrane
Taager Tech Blog
Published in
3 min readJul 19, 2022

🤔 Why Kotlin Multiplatform?

Write once, run everywhere is an old desire in software development. Sun Microsystems created the slogan to illustrate the cross-platform benefits of the Java language back in 1995.

We often try to reuse as much code as possible, optimizing the development time. As a result, we have fewer headaches while fixing bugs and scaling our systems.

Our product development initially started targeting the Android platform, and our state was:

  • We had the knowledge and expertise of the Kotlin language that Google has supported since 2017, making it the first-class language for Android development a year after.
  • A good amount of our codebase contained presentation logic and data orchestration. So if we had that code shared, we would avoid duplicating around 75% of the code.

The closest option to our needs was Flutter, as we could code once and target both Android and iOS platforms. But it had some red flags for us:

  • Dart language (Not as powerful as Kotlin, no Flutter or Dart experience in the team)
  • For platform-specific implementations (wrappers), we still need language knowledge (Kotlin / Swift) and framework knowledge (Android / iOS) (e.g., Notifications, Camera APIs, etc.)

With Kotlin Multiplatform — KMP, we could facilitate non-UI logic across multiple platforms while at the same time allowing the User Interface to be built using the native frameworks without compromising the user experience. This way, we can apply the same architecture in a good part of the project, e.g., building the domain and data layers and sharing them between Android and iOS.

For iOS developers, Kotlin has a lot of similarities with Swift, so onboarding them wouldn’t be hard.

Using KMP’s expect/actual feature would allow us to have different behaviors for each platform while expecting the same interface within the common code.

🧐 How is KMP in Taager?

Here at Taager, we opted to use Clean as the overall architecture because it defines good boundaries between the layers and fits well on what we want to achieve on a multiplatform level. A multiplatform architecture builds the UI natively and shares all the rest; data, domain, and presentation layer.

We also have our own Unidirectional Data Flow architecture in the presentation layer, codenamed Circuit (more on this soon), which, in combination with the native UI toolkits (Jetpack Compose and Swift UI), makes it possible to have presentation logic as shared code. As a result, screens’/views’ responsibility becomes as simple as rendering UI states and navigating to other screens.

Currently, at the time of writing of this article, Taager Merchant and Warehouse mobile apps are in production, with a big part of the code following KMP practices, while also two more apps are in the works, and planned for the near future.

What have we used to achieve the above?

  • DI: Kodein
  • Network: Ktor
  • Storage: SQLDelight / MultiplatformSettings
  • Reactive streams: Reaktive / Coroutines

🤩 Conclusion

Finding a solution for coding once and shipping everywhere is not simple and varies among companies. KMP has worked very well at Taager. Choosing the right architecture helped us to only have the UI built natively and use platform-specific code only when really needed.

Takeaways:

Positive 😌👍🏻

  • Write platform agnostic code allows a lot of shared code across supported platforms
  • Use the same architecture in most parts of the project

Negative 😓👎🏻

  • Finding Multiplatform libraries that are stable and easy enough to use has been a challenge. That also applies to the unit tests. We currently only run the test suites on the JVM due to the lack of better options for mocking and fixturing objects.
  • Handling of different memory models when targeting native (iOS), e.g. Enforcing freezing objects, while sharing objects between threads (the new memory manager solves it)

Thanks to André Mion

--

--