TNS
VOXPOP
Do You Resent AI?
If you’re a developer, do you resent generative AI’s ability to write code?
Yes, because I spent a lot of time learning how to code.
0%
Yes, because I fear that employers will replace me and/or my peers with it.
0%
Yes, because too much investment is going to AI at the expense of other needs.
0%
No, because it makes too many programming mistakes.
0%
No, because it can’t replace what I do.
0%
No, because it is a tool that will help me be more productive.
0%
No, I am a highly evolved being and resent nothing.
0%
I don’t think much about AI.
0%
Frontend Development

How to Handle Platform-Specific Dependencies in Kotlin Multiplatform

Learn how to set up Kotlin Multiplatform with Koin dependency injection to simplify adopting shared features across Android and iOS apps.
May 16th, 2024 11:30am by
Featued image for: How to Handle Platform-Specific Dependencies in Kotlin Multiplatform
Featured image by NSYS Group on Unsplash.

Kotlin Multiplatform (KMP) enables developers to gradually adopt shared features for cross-platform projects, without having to completely overhaul the existing codebase. This technology’s flexibility and versatility make KMP an exceptional, thoughtfully designed framework.

If you’re new to the Kotlin Multiplatform framework, Get Started with Kotlin Multiplatform is an invaluable resource.

In specific scenarios, especially when building shared features in KMP projects, a need arises to implement particular dependencies in KMP targets. Consider, for example, a scenario where a KMP or Compose Multiplatform application targets Android and iOS platforms. There are a few options if you need a particular dependency for both platforms. On Android, you can add the Android dependency, as you would in a typical Android application, but on iOS, you have several options:

  • Check if the iOS dependency is part of the prebuilt platform libraries in Kotlin Multiplatform projects in the Kotlin Native libraries (e.g., Core Foundation, Core Data, Cloud Kit). In this case, no additional configuration is required; read Adding iOS Dependencies for more information.
  • Integrate with Kotlin’s CocoaPods dependency manager, then import and call the pod dependencies directly from your Kotlin code — this is recommended according to the Kotlin docs. When using this method, note that Kotlin supports interoperability with Objective-C dependencies and Swift dependencies only if their APIs are exported to Objective-C with the @objc attribute. Pure Swift dependencies or Swift pods are not yet supported. While you could download the library directly and add it to your source set, I don’t recommend that option. That said, using CocoaPods is an excellent option, and there are many articles and posts covering it that you can find with a simple internet search. Using CocoaPods along with checking the Kotlin libraries for dependencies enables you to leverage expected/actual implementations or the dependency injection (DI) framework to utilize iOS dependencies.
  • Using platform-specific APIs through a shared interface implemented in both Kotlin and Swift is often relevant for existing third-party libraries. These APIs are passed from the target platform’s modules into the shared module. The official Kotlin documentation advises checking for a Multiplatform library alternative before using platform-specific APIs. This is sound advice, and I strongly recommend exploring the kmp-awesome repository to search for suitable Multiplatform libraries. However, given that Kotlin Multiplatform is still evolving, many libraries lack an official or recommended KMP version or a reliable alternative with guaranteed long-term support. These scenarios prompt the need to discuss implementing shared platform components in a KMP project and provisioning and implementing platform-specific APIs, as I’ll explore in this article series.

This article series also covers:

  • How to create an ApplicationComponent to use as a bridge for passing components between target (platform) modules and the shared module.
  • How to use Koin as a DI library to inject these components.
  • How to implement a simple network listener on Android and iOS for a KMP project.

To help guide you, I have created a video to showcase the result of your work.

Getting Started: Introducing the ApplicationComponent

The term ApplicationComponent refers to a class designed with properties that can be implemented or utilized by either the Android or iOS platform. The naming and concept were inspired by the structure used in Chris Banes’ Tivi app repository on GitHub.

To kick things off, initiate a new project with the Kotlin Multiplatform Wizard. If you are unfamiliar with Kotlin Multiplatform’s folder structure or project layout, read The Basics of Kotlin Multiplatform Project Structure first to learn how a KMP application is organized and the purpose of each component.

This example sets up a Compose Multiplatform project. Each platform will have its bespoke component: AndroidApplicationComponent for Android and iOSApplicationComponent for iOS.

After creation, your project’s architecture will be organized as follows:


Begin with the composeApp module. Within the androidMain and iosMain source sets, create a new directory or package named platform. In this location, define your ApplicationComponent classes. In the same file, implement a function named application that will accept the respective ApplicationComponent as its argument.

You’ve laid the groundwork; now it’s time to set up the entry point in each source to initialize each component. Begin with the androidMain source set. Here, create an Application class named ExampleApp. Make sure to declare this class in your AndroidManifest.xml. Then, emulating the conventions in the JetBrains (JB) documentation, invoke the application function you previously defined.

Here’s how it’s done:

The entry point to the iOS application can be accessed through the iOSApp directory. Simply search for the IOSApp.swift file and make these changes:

And there you have it — a platform bridge that can transfer components from various platforms into your shared module. Typically, these components could be shared interfaces with platform-specific implementations or particular platform-specific dependencies, like UserDefaults on iOS or the Android Application class. It’s worth noting that this architecture will slightly change once you incorporate DI.

To cap it off, here’s a snapshot of how the project structure appears after implementing these modifications:


NOTE: Given that the Compose UI is shared between both platforms, the androidApp module isn’t included, allowing you to establish all Android-specific dependencies directly within the androidMain source set via DI. As a result, for the scope of this article, I won’t be utilizing the AndroidApplicationComponent. However, this component may still be relevant in different contexts, such as incrementally integrating KMP in projects with distinct user interfaces (UIs) for each platform.

Dependency Injection: Using Koin to Aid Component Injection

As you scale up and the component count increases, streamlining dependency provision becomes essential, and this is where the Koin DI framework enters the picture. If you’re not familiar with Koin or are transitioning from Dagger, Hilt or another DI framework, I strongly suggest perusing the Koin official documentation for comprehensive tutorials and guides.

Koin DI centralizes the initialization of your components within the platformModule. The setup is straightforward — simply adhere to the steps provided by the JB team. I’ll delve deeper into this process in the second article in this series, when I explain how to integrate your first dependency.

To kickstart Koin in your project, begin in the commonMain source set by creating a new package named di. Then proceed with the following setup:

With the foundation now set, you can use Koin across different platforms.

First and foremost, it’s crucial to ensure that the ApplicationComponents for both platforms are correctly injected into their respective dependency graphs. This approach guarantees that you can consistently access any additional components linked to them. For the sake of organization, I prefer to separate the initialization process; thus, I’ll create KoinInit.android.ktand KoinInit.ios.kt files. This step, while not mandatory — especially in the androidMain source set — is more about personal coding style and preference.

Next, return to your ExampleApp.kt file in the Android project and initiate Koin. The setup should look something like this:

And voilà, that’s all there is to it! With these steps, the AndroidApplicationComponent is now integrated into the DI graph. The process for iOS follows a similar pattern. On the iOS side, you’ll employ another method to supply additional modules from the iosApp module through the IosApplicationComponent. This method, initKoinIos, will essentially add the IosApplicationComponent as an extra module exclusively for the iOS shared source set.

Utilize it in your Swift code like this:

Now you have laid the foundation for Kotlin Multiplatform. In the second part of this series, I’ll explain how to utilize these components, particularly the IosApplicationComponent, to implement a simple network listener on Android and iOS.

The shift towards remote and borderless hiring enables companies to look beyond geographical constraints to access global talent. Explore strategies to build high-performing, globally distributed teams by downloading The Future of Hiring Is Borderless.

Group Created with Sketch.
TNS owner Insight Partners is an investor in: Simply.
TNS DAILY NEWSLETTER Receive a free roundup of the most recent TNS articles in your inbox each day.