How to Use Swift Packages in Kotlin Multiplatform using Koin
When developing Kotlin or Compose Multiplatform applications, sometimes dependencies are not supported in Kotlin Multiplatform, and we need a way to make them work. There are several ways to use Swift dependencies in Kotlin Multiplatform. If we’re lucky and the library has a CocoaPods dependency, some libraries expose Objective-C headers and can be used directly in Kotlin by including the CocoaPods dependency. But sometimes, that is not the case, and we are limited to writing Swift code only. However, we still need to provide this Swift implementation to our Kotlin Multiplatform project.

In this blog post, I’ll go through using Firebase Analytics as an example, but this approach can be applied to any Swift dependency.
Step 1: Create a Common Analytics Interface
First, we need to create a common Analytics
interface:
interface Analytics {
fun logEvent(event: String, params: Map<String, Any>? = emptyMap())
fun setEnabled(enabled: Boolean = true) //Can be used based on user consent
companion object {
const val EVENT_SCREEN_VIEW = "screen_view"
const val PARAM_SCREEN_NAME = "screen_name"
}
}
You can also add commonly used event names here. I also like to create an extra extension function to log each screen view:
fun Analytics.logScreenView(screenName: String, params: Map<String, Any>? = emptyMap()) {
logEvent(
event = Analytics.EVENT_SCREEN_VIEW,
params = mapOf(Analytics.PARAM_SCREEN_NAME to screenName) + (params ?: emptyMap())
)
}
Step 2: Implement Analytics for Each Platform
Android Implementation
In the androidMain
source set, create FirebaseAnalyticsImpl
:
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
class FirebaseAnalyticsImpl(private val firebaseAnalytics: FirebaseAnalytics) : Analytics {
override fun logEvent(event: String, params: Map<String, Any>?) {
val bundle = Bundle().apply {
for (entry in params ?: emptyMap()) putString(entry.key, entry.value.toString())
}
firebaseAnalytics.logEvent(event, bundle)
}
override fun setEnabled(enabled: Boolean) {
firebaseAnalytics.setAnalyticsCollectionEnabled(enabled)
}
}
iOS Implementation
Since we don’t have CocoaPods dependencies in Kotlin Multiplatform, we need to create a Swift file in the iOS project. First, make sure you add FirebaseAnalytics dependency using Swift Package Manager. Then, in iosApp
, create a new Swift file called FirebaseAnalyticsImpl
:
import Foundation
import ComposeApp
import FirebaseAnalytics
import FirebaseCore
class FirebaseAnalyticsImpl: ComposeApp.Analytics {
func logEvent(event: String, params: [String : Any]?) {
var eventParams: [String: Any] = [:]
params?.forEach { key, value in eventParams[key] = "\(value)" }
Analytics.logEvent(event, parameters: eventParams)
}
func setEnabled(enabled: Bool) {
Analytics.setAnalyticsCollectionEnabled(enabled)
}
}
Step 3: Provide Implementations to Kotlin Multiplatform with Koin
Koin makes dependency injection easier. I assume you’re already familiar with providing platform-specific modules using Koin in Kotlin Multiplatform. If not, check out my blog post:
Achieving Platform-Specific Implementations with Koin in KMP
Android Side
In platformModule
, provide the implementation as a singleton:
internal actual val platformModule: Module = module {
single { FirebaseAnalyticsImpl(firebaseAnalytics = Firebase.analytics) } bind Analytics::class
}
iOS Side
Since Kotlin can’t directly access Swift classes, we need a factory to create instances. First, create SwiftLibDependencyFactory
in iosMain
:
interface SwiftLibDependencyFactory {
fun provideFirebaseAnalyticsImpl(): Analytics
}
Then, in iosApp
, create SwiftLibDependencyFactoryImpl.swift
:
import Foundation
import ComposeApp
import SwiftUI
import UIKit
class SwiftLibDependencyFactoryImpl: SwiftLibDependencyFactory {
static var shared = SwiftLibDependencyFactoryImpl()
func provideFirebaseAnalyticsImpl() -> any Analytics {
return FirebaseAnalyticsImpl()
}
}
Although this requires some boilerplate code, the advantage is that you only need to do this once. When adding a new Swift library, just add a function in SwiftLibDependencyFactory
and provide its implementation.
Step 4: Connect Everything in Kotlin Multiplatform
In iosMain
, add a new koin module to manage the dependency:
internal fun swiftLibDependenciesModule(factory: SwiftLibDependencyFactory): Module = module {
single { factory.provideFirebaseAnalyticsImpl() } bind Analytics::class
}
Step 5: Provide SwiftLibDependencyFactoryImpl
to Koin
In iosMain main.kt
, add an extension function to provide the module to the application:
fun KoinApplication.provideSwiftLibDependencyFactory(factory: SwiftLibDependencyFactory) =
run { modules(swiftLibDependenciesModule(factory)) }
You probably initialize dependencies in your project something similar to this:
object AppInitializer {
fun initialize(onKoinStart: KoinApplication.() -> Unit) {
startKoin {
onKoinStart()
modules(appModules)
}
}
}
In your Swift application, call:
AppInitializer.shared.initialize(onKoinStart: { koinApp in
koinApp.provideSwiftLibDependencyFactory(
factory: SwiftLibDependencyFactoryImpl.shared
)
})
Step 6: Use Analytics
Now that everything is set up, you can inject Analytics
anywhere in your Kotlin code. For example, in a Composable function:
val analytics = koinInject<Analytics>()
analytics.logEvent(event = "PurchaseButtonClick")
Make sure to call analytics.setEnabled(true)
at app startup or after obtaining user consent.
This approach ensures integration of any Swift dependencies into Kotlin Multiplatform while keeping everything organized using Koin in Kotlin Multiplatform project. And if you’re using KAppMaker, the boilerplate for this is already set up for you, so you can get started even faster.