Chapter 9: Integrating KMM into Existing Android and iOS Apps

So far, we've discussed the KMM tech and how it works. While adopting KMM into apps in production, you'll probably have some questions about the implications of KMM on the logistics of app development. In this chapter, we're going to focus on the DevOps perspective of KMM and discuss the following topics:

  • Deciding on a mono repository or a shared library
  • Exploring team structure and tooling
  • Learning some adoption tips

By the end of this chapter, you should be prepared to take the next steps and adopt KMP in your projects.

Deciding on a mono repository or a shared library

In this section, we're going to reason about the pros and cons of the following two repository setups:

  • Mono Repository: This is where the shared code is just a module/submodule. This is the choice of most KMM/KMP example projects.
  • Multiple Repositories: This is where the shared code is like a library that's consumed by the different platforms; that is, Android and iOS. Most production apps will likely see this option as more attractive.

Let's start by looking at mono repositories.

Mono repository

In a mono repository structure, your shared code, the Android app, and the iOS app are all contained in the same repository, as shown in the following diagram:

Figure 9.1 – Mono repository

Figure 9.1 – Mono repository

Some of the KMP examples out there, such as the ones where code is shared not just between the different frontend applications but in a server-client/backend-frontend mode, could inspire people to create a more enhanced QA process of the communication layer between the client and the server:

  • A communication protocol could be enforced between the backend and the frontend in the form of data transfer objects (DTOs) and endpoints defined in this shared KMP module.
  • A CI pipeline would "govern" the operability of this communication protocol. If you need to make a change to the communication protocol to suit the backend, you will be able to see how this affects frontend applications.

Having something like this could shorten the feedback loop of backend changes, which usually requires direct communication between the backend and the frontend.

One other big advantage of a mono repository structure is that you can avoid the overhead of publishing the shared library for different types of consumers.

One of the biggest drawbacks is probably how it scales as having one big repository for multiple purposes may not be ideal for big projects and teams.

This option is also probably not something that published production apps would likely consider; they usually have different repositories for their different platforms and they would only share a small part of their business logic to mitigate risk.

Multiple repositories

In a multiple repository structure, the shared code is isolated from the apps and is contained in a different repository, as shown in the following diagram:

Figure 9.2 – Multiple repositories

Figure 9.2 – Multiple repositories

Teams with established production apps are often more thoughtful of making big changes and refactoring, and rightly so. It makes sense for apps that are not starting on fresh grounds to experiment with KMP in a more isolated way. This helps keep the KMP world in an isolated repository that's consumed by the platforms in a platform-specific way. Here, iOS doesn't need Kotlin and/or Gradle-related tools to make sense of the shared code. They receive the library, ready to be consumed.

This setup can reduce the risk of experimenting with KMP, but at the same time has more complexity around maintaining the shared library as you need to manage an additional publishing and versioning process. For many production apps, this can be overkill.

To reduce this complexity, in the following subsections, we're going to discuss the different ways you can publish and consume a shared KMP library. We'll focus on iOS since publishing and consuming a shared KMP library is the same as for any Kotlin/JVM library, which is well documented already. In short, we can categorize the publishing formats as follows:

  • The Unreadable Shared Code: Easy to consume but hard to read and debug
  • The Readable Shared Code: Moderately easy to consume; easy to read and debug
  • The Modifiable Shared Code: Harder to consume but easy to read, debug, and modify

Publishing binaries

For most teams, this approach sounds the most attractive as it isolates the KMP world and the shared library from the platform-specific Android and iOS apps.

This means that from the iOS consumer's perspective, you've chosen not to make compromises on how you consume external libraries – you'll stick with your currently preferred choice, be it Carthage, CocoaPods, or Swift Package Manager.

From the publisher's perspective, you'll need to create a binary that is well understood by your preferred dependency manager.

Fortunately, since Kotlin 1.5.30, KMP supports XCFrameworks (the replacement for fat/universal frameworks in the Apple ecosystem). We won't dive into the details of the differences between XCFrameworks and universal frameworks, but Carthage, Cocoapods, and Swift Package Manager support XCFrameworks, which means you can produce a required binary format and pull it in with your preferred dependency manager on iOS.

You can use the assembleXCFramework Gradle task to generate such a framework. You can find out more at https://kotlinlang.org/docs/whatsnew1530.html#support-for-xcframeworks.

One of the main drawbacks of publishing binaries is due to the isolation of the Kotlin world:

  • It isolates the iOS team from the shared code as they view it as just another external library, while it makes the shared Kotlin code more unreachable.
  • Debugging that shared code becomes problematic since the Kotlin/Native compiler adds absolute paths to files for its debugging purposes. So, binaries that have been built locally will work, but external binaries that have been pulled in won't.

This means that, in practice, you and your team are probably better off if your team has an easy way to enter the Kotlin world. This is what the guys at Touchlab worked on and the approach needs some applause.

Xcode-Kotlin plugin

You can find the Xcode-Kotlin plugin at https://github.com/touchlab/xcode-kotlin.

The main benefit of using this plugin is that it doesn't enforce a whole new ecosystem on iOS developers. It provides a lightweight tool on top of Xcode that they can use to debug and become familiar with the Kotlin code.

Once the plugin has been set up, it regularly clones the shared code, which can then be integrated into your current Xcode project structure. The plugin provides both debugging support and syntax highlighting for this Kotlin code.

Now, depending on your team, you may want the full experience and would like your iOS developers to become accustomed to the Kotlin ecosystem. We'll explore that option next. But in practice, it's probably best to start with the plugin first, monitor the developer experience, and if the interest is there, include them in the game even more so that they don't just "read" the shared code.

Klibs

We will be focusing on klibs in this chapter as it will probably become the standard for sharing Kotlin code in a Kotlin way.

Klib is a pure Kotlin format for distributing Kotlin code and it's a common denominator across all backends. In Chapter 2, Exploring the Three Compilers of Kotlin Multiplatform, we discussed the new backend compiler strategy of working with an intermediate representation (IR) of the code and producing an executable from that. This IR is the klib format. This new format standardizes how you can publish the different targets of the shared code:

Figure 9.3 –  The klib format

Figure 9.3 – The klib format

Kotlin bundles the target source sets with common when compiling, so jvmMain is bundled with commonMain. One of the goals of the KMP developers from JetBrains is to make source sets a proper compilation unit so that commonMain will be compiled to a separate klib that is a dependency of jvmMain. This way, you can distribute the shared code as commonMain only and plug in the supported platform as you wish.

Using this format may need the most tooling from the shared code consumers, but it will give them the most flexibility.

Conclusion

When it comes to using KMM, where the main goal is to share code between Android and iOS, you should choose a mono repository or a shared library based on the following approaches:

  • 0 to 100 approach: If you already have an Android and iOS app but would like to start sharing code in small steps, it probably makes sense not to restructure everything but to create your shared code separately and pull that in.
  • 100 to 0 approach: If you're starting fresh and would like to share as much as possible, going with a mono repository will probably be more advantageous for you.

    Note

    The number "100" doesn't suggest that you can get to one single code base with two apps. In reality, this will most likely top out at around 80% (maximum), but who knows what Jetpack Compose will bring – it may not only be possible but also worthwhile to write cross-platform with Jetpack Compose.

Any kind of change requires some type of adaptation, and KMP will probably have an impact on your team's structure and tooling and make some people uncomfortable. I'd argue that most of these inconveniences are similar to moving into a bigger bedroom while complaining that the light switch is far from the bed. Nevertheless, it's good to prepare yourself before you find out that, after moving into a mansion, you don't have the resources to heat it.

Exploring team structure and tooling

If you're planning on adopting KMP in your team, the following points may be obvious to you by now, but it's still worth pointing them out:

  • Your shared code needs mostly Kotlin and Gradle-related expertise.
  • Android teams will mostly feel natural about working with the shared code, with a relatively small amount of learning needed for KMP specifics.
  • iOS teams will have a harder time, even though Kotlin and Swift are not too different. This is especially true when it comes to a new build tool, integrated development environment (IDE), and ways of working.

Team structure

Because of the aforementioned points, you should probably evaluate your team structure and plan carefully so that your shared code doesn't end up being a huge bottleneck that only a few people of your Android team will touch; it will inevitably drive your shared code toward Android and you want it to be unbiased toward platforms.

One example of a team structure I feel suits KMP is the one that the JetBrains Space team used:

  • Have a dedicated team for the shared code with Kotlin and KMP expertise, focusing on the business logic. This team doesn't need expertise in the relevant platforms.
  • Have dedicated platform teams (Android, iOS, and the web) that have expertise in the given platform and know the ins and outs of the specific framework.

To learn more about KMP in terms of JetBrains Space, Maxim's Kotlin in Space talk at the 2019 KotlinConf is a good watch: https://www.youtube.com/watch?v=JnmHqKLgYY4&t=25m30s.

If your team doesn't have the resources to pull off this team structure, it's fine to have the shared code experts from your Android team. Just make sure that the iOS team is part of the conversations and that they are not overly pushed to learn Kotlin and Gradle. Many good developers will probably be open to learning about them if they grasp the value proposition of KMP.

Tooling

A lot of people tend to overlook tooling, but bad or incorrectly used tooling can become a burden if it disrupts a developer's workflow. In this section, I'm going to try my best to help you to leverage the most essential tooling out there for your KMP journey.

Choosing the right IDE

If you are the Notepad/Vim + command line type, you can skip this section. Here, I'll mostly be talking about my experience with suffering through some situations in an experimental world.

So far, IntelliJ IDEA seems to be the safest choice when it comes to setting up and configuring KMP projects while offering more flexibility than just a KMM project setup. I would use IntelliJ IDEA whenever I wanted to check out a more sophisticated KMP project setup or if I was having strange build configuration issues.

Android Studio with the KMM plugin is also really promising and it is becoming better and better. For KMM applications, I'd probably use the project template offered by the KMM plugin. It can also provide a nice IDE experience for trying out and debugging the shared code on both Android and iOS. While it seems to be maturing, especially in the early days, it didn't have the stability for me to debug and run the shared code on an iOS simulator properly. Also, because syntax highlighting and code completion for Swift/Obj-C is not supported out of the box (you can improve on syntax highlighting with an IntelliJ plugin), to get the full experience on iOS, I always have Xcode set up.

So, personally, for an all-round experience of working and testing the shared code, I recommend the following IDE setup:

  • IntelliJ IDEA for KMP-related build configurations. This may be needed less and less, but if you're having trouble with the setup, I'd try it out.
  • Android Studio with the KMM plugin for setting up a KMM app and running/debugging shared code on your apps.
  • Xcode for the full iOS experience (Swift/Obj-C coding, managing pods, and build configurations) and possibly the Xcode-Kotlin plugin for convenient debugging on iOS.

If you want a "one" IDE experience, AppCode is a JetBrains product that may have the potential to become the KMM IDE in the future. They recently announced a KMM plugin for AppCode that lets you build, run, and debug a KMM app on both Android and iOS while having syntax highlighting and code completion capabilities for both Kotlin and Swift/Obj-C.

Debugging

We've touched on the topic of debugging your shared code a little already. On iOS, since debugging on Android isn't that different, I'd recommend either or both of the following plugins:

  • The Android Studio KMM plugin or AppCode KMM plugin
  • Touchlab's Xcode-Kotlin plugin, for iOS developers that want to stay as Kotlin ecosystem-free as possible

Crash reporting

Since Kotlin propagates errors/exceptions differently compared to Swift/Obj-C, shared code-related crashes may be hard to read when you're running on iOS. For this purpose, Touchlab created a nice library for making sure that these errors are still readable: https://github.com/touchlab/CrashKiOS.

Kevin gave a nice talk on these production questions, some of which may be or become outdated. Nevertheless, I highly recommend his talk as some of the aforementioned topics are also based on their experience: https://www.youtube.com/watch?v=hrRqX7NYg3Q.

In the next section, I will offer some of my personal views on how teams could leverage the benefits of KMP while lowering the risk of its adoption.

Learning some adoption tips

Have you decided that KMP is for you and your team and would like to try it out? Here are a couple of quick tips to help you mitigate risk and gradually descend into the KMP world:

  • Kotlin/JVM is already used widely for Android development, which you can leverage. You can start by using Android while you introduce platform-agnostic concepts and isolate components that shouldn't depend on the Android framework.

Many teams already do this by having Java/Kotlin modules for their business logic. These teams are already one step closer to making these modules shareable and doing this doesn't need any KMP expertise and doesn't introduce KMP specific risks.

  • You can then start educating your Android team on KMP and gradually make your components platform-agnostic. Make sure that iOS is involved in the communication as they will be one of the consumers of the shared code. I'd encourage iOS people to learn about KMP and contribute, but without forcing it onto anyone.
  • When you have your platform-agnostic code, you need to try out the consumer experience from iOS. This can be done iteratively, where you adapt your shared code so that consuming from iOS brings the best experience.
  • If you are starting a new project, possibly for a startup, I'd push for having Android first, which has a proper architecture that can be shared with iOS. Then, you can bring that shared code to life on the iOS side.
  • Since the knowledge gap between the Android world and KMP is not big, Android developers, especially ones with Kotlin knowledge, will probably find learning KMP easy. But it's not a stable framework yet, which means that some uncomfortable learning periods will occur in some scenarios and that you will need to get out of the comfort of reading well-prepared documentation.

To shorten the learning curve and help out with undocumented issues, turning to and collaborating with already experienced KMP developers is a good shortcut.

  • While KMP is not stable, you can already leverage its advantages in production apps. Many big companies already use KMP, such as Netflix, Philips, and Leroy Merlin, and you can find a list, along with case studies for each, on the official Kotlinlang website: https://kotlinlang.org/lp/mobile/case-studies/.

So, the biggest question isn't "is KMP ready for production?" but "are you ready for KMP?".

And without being arrogant, what I mean by this is that the hardest thing with KMP is finding the right guidance since the road isn't that well known. In the next chapter, I'll share some of the things that worked out for me when it came to learning about KMP back in 2019, when even setting up a KMP project was pretty hard.

  • Again, try not to overuse the expected/actual declarations as they are only known to the KMP world and they specify platform-specific abstractions. Using a general abstraction method such as interfaces/protocols can be much more flexible. For example, you can inject implementations from Swift and Kotlin modules that don't know about KMP-specific concepts.

Summary

In this chapter, we dived deeper into production-related questions, as well as KMP's influence on team structure.

We discussed the pros and cons of a mono repository versus a shared library and explained which one may be more suitable for you and your team.

Then, we explored how KMP could influence your team structure and the current state of the KMP ecosystem in regards to tooling. We also discussed some adoption tips that, given KMP's nature and based on your situation, may help you adopt KMP properly, with low risk and high upside.

I hope that you now have a much better picture of what to expect when you try to adopt KMP in your new or existing apps. In the next chapter, we'll go over what we've learned throughout this book and where to look for future knowledge.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.206.68