Kotlin Multiplatform Development Help

Integration with the UIKit and SwiftUI frameworks

Compose Multiplatform is interoperable with both UIKit and SwiftUI frameworks. You can embed Compose Multiplatform within an iOS application as well as embed native iOS components within Compose Multiplatform. This page provides some examples of how you can do this when working with UIKit and SwiftUI frameworks:

Use Compose Multiplatform inside a SwiftUI application

To use Compose Multiplatform inside a SwiftUI application, create a Kotlin function MainViewController() that returns UIViewController from UIKit and contains the following Compose Multiplatform code inside:

fun MainViewController(): UIViewController = ComposeUIViewController { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("This is Compose code", fontSize = 20.sp) } }

ComposeUIViewController() is a library function from Compose Multiplatform that accepts another function passed using the content argument. This function can call other composable functions, for example, Text().

Next, you need a structure that represents Compose Multiplatform in SwiftUI. Create the following ComposeViewstructure that converts UIViewController to a SwiftUI view:

struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { Main_iosKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }

Now you can use the ComposeView structure in other SwiftUI structures.

In the end, your application should look like this:

ComposeView

In addition, you can use this ComposeView in any SwiftUI view hierarchy and control its size from within SwiftUI code.

If you want to embed Compose Multiplatform into your existing applications, use the ComposeView structure in any place where SwiftUI is used. For an example, see our sample project.

Use Compose Multiplatform inside a UIKit application

To use Compose Multiplatform inside a UIKit application, add your Compose Multiplatform code to any container view controller. This example uses Compose Multiplatform inside the UITabBarController class:

let composeViewController = Main_iosKt.ComposeOnly() composeViewController.title = "Compose Multiplatform inside UIKit" let anotherViewController = UIKitViewController() anotherViewController.title = "UIKit" // Set up the UITabBarController let tabBarController = UITabBarController() tabBarController.viewControllers = [ // Wrap them in a UINavigationController for the titles UINavigationController(rootViewController: composeViewController), UINavigationController(rootViewController: anotherViewController) ] tabBarController.tabBar.items?[0].title = "Compose" tabBarController.tabBar.items?[1].title = "UIKit"

With this code, your application should look like this:

UIKit

Explore the code for the example in this sample project.

Use UIKit inside Compose Multiplatform

To use UIKit elements inside Compose Multiplatform, add the UIKit elements that you want to use to UIKitView from Compose Multiplatform.

You can write this code purely in Kotlin or use Swift as well. In this example, UIKit's MKMapView component is displayed in Compose Multiplatform. Set the component size by using the Modifier.size(...) or Modifier.fillMaxSize() functions from Compose Multiplatform:

UIKitView(modifier = Modifier.size(300.dp), factory = { MKMapView() })

With this code, your application should look like this:

MapView

Now, let's look at an advanced example. This code wraps UIKit's UITextField in Compose Multiplatform:

var message by remember { mutableStateOf("Hello, World!") } UIKitView( factory = { val textField = object : UITextField(CGRectMake(0.0, 0.0, 0.0, 0.0)) { @ObjCAction fun editingChanged() { message = text ?: "" } } textField.addTarget( target = textField, action = NSSelectorFromString(textField::editingChanged.name), forControlEvents = UIControlEventEditingChanged ) textField }, modifier = modifier.fillMaxWidth().height(30.dp), update = { textField -> textField.text = message } )

The factory parameter contains the editingChanged() function and the textField.addTarget()listener to detect any changes to UITextField. The editingChanged() function is annotated with @ObjCAction so that it can interoperate with Objective-C code. The later action parameter passes the name of the function that should be called in response to a UIControlEventEditingChanged event.

The next interesting part is the update parameter:

update = { textField -> textField.text = message }

This function is called when the observable message state changes its value. The function updates the internal state of the UITextField so that the user sees the updated value.

Explore the code for this example in this sample project.

Use SwiftUI inside Compose Multiplatform

To use SwiftUI inside Compose Multiplatform, add your Swift code to an intermediate UIView. Currently, you can't write SwiftUI structures directly in Kotlin. Instead, you have to write it in Swift and pass it to a Kotlin function.

To begin with, add an argument to create ComposeUIViewController:

fun ComposeEntryPointWithUIView(createUIView: () -> UIView): UIViewController = ComposeUIViewController { Column( Modifier .fillMaxSize() .windowInsetsPadding(WindowInsets.systemBars), horizontalAlignment = Alignment.CenterHorizontally ) { Text("How to use SwiftUI inside Compose Multiplatform") UIKitView( factory = createUIView, modifier = Modifier .size(300.dp) .border(2.dp, Color.Blue), ) } }

Then, pass createUIView to your Swift code like this:

Main_iosKt.ComposeEntryPointWithUIView(createUIView: { () -> UIView in UIView() // An empty view for example })

To create an intermediate UIView that allows you to interoperate between SwiftUI and UIKit, create the following SwiftUIInUIView class:

class SwiftUIInUIView<Content: View>: UIView { init(content: Content) { super.init(frame: CGRect()) let hostingController = UIHostingController(rootView: content) hostingController.view.translatesAutoresizingMaskIntoConstraints = false addSubview(hostingController.view) NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } }

You can then use it in SwiftUI and add it to any Swift code inside:

Main_iosKt.ComposeWithUIKitView(createUIView: { () -> UIView in SwiftUIInUIView( content: VStack { Text("SwiftUI in Compose Multiplatform") } ) })

In the end, your application should look like this:

UIView

Explore the code for this example in this sample project.

Last modified: 01 November 2023