Quick Way to Run Swift Inside KMP Without Writing Bindings

There are times when you need to run some Swift code inside a Kotlin Multiplatform project but writing bindings for it or adding a plugin that does that just seems too much. I’ve found that defining an interface inside Kotlin and providing its implementation inside Swift is a good solution in such situations.

First, create the interface and define any functions that should be ran inside Swift.

interface SwiftInteropHelper {
    fun getVariableFont(width: Double, weight: Double, size: Double): UIFont
}

Then, create a CompositionLocalProvider so the interface can be easily accessed anywhere. The MainViewController will now give an instance of it that will be created in Swift.

val LocalSwiftInteropHelper = staticCompositionLocalOf<SwiftInteropHelper> {
    error("No swift interop helper provided")
}

fun MainViewController(
    swiftInteropHelper: SwiftInteropHelper
) = ComposeUIViewController {
    // Make the view factory globally accessible
    CompositionLocalProvider(LocalSwiftInteropHelper provides swiftInteropHelper) {
        Main()
    }
}

Now implement the interface, make sure to import ComposeApp and first build the gradle project inside Android Studio so Xcode can see the interface. Also create a shared instance of it that will be passed to MainViewController.

import Foundation
import UIKit
import ComposeApp

class SwiftInteropHelperImpl: SwiftInteropHelper {
    static var shared = SwiftInteropHelperImpl()
    
    func getVariableFont(width: Double, weight: Double, size: Double) -> UIFont {
        return ...
    }
}

Pass the instance to MainViewController.

struct ComposeView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        MainViewControllerKt.MainViewController(
            swiftInteropHelper: SwiftInteropHelperImpl.shared
        )
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

And that’s it, after the initial setup it’s very easy to add new functions if the need comes.