DEV Community

RockAndNull
RockAndNull

Posted on • Originally published at paleblueapps.com on

A primer into native interactions in Compose Multiplatform apps

A primer into native interactions in Compose Multiplatform apps

Kotlin Multiplatform (KMP) offers a powerful way to write shared code across multiple platforms while still giving you the flexibility to define platform-specific implementations. A key part of achieving this is using the expect/actual mechanism, which allows you to define common APIs in your shared module and provide platform-specific implementations in the respective targets.

This approach is particularly handy when building Compose Multiplatform apps, where you might need to perform native system interactions like opening a URL in the browser or sharing text with the native operating system mechanism.

In this blog post, we'll explore how to set up and use the expect/actual pattern to create a SystemService that handles these interactions seamlessly across platforms and uses a Koin injection for dependency injection. By the end, you'll see how this technique keeps your codebase clean and efficient while enabling platform-specific capabilities.

The interface

interface SystemService {
    fun launchUrl(url: String)
    fun shareText(details: String)
}

expect fun Module.factorySystemService(): KoinDefinition<SystemService>

Enter fullscreen mode Exit fullscreen mode

SystemService.kt

A pure interface that defines what interactions we need with the platform.

We use Koin for dependency injection and we define a module that needs to be implemented in each platform (expect).

The implementations

internal class AndroidSystemService(
    private val context: Context
) : SystemService {

    override fun launchUrl(url: String) {
        if (url.isBlank()) return
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        intent.resolveActivity(context.packageManager)?.let {
            context.startActivity(intent)
        }
    }

    override fun shareText(details: String) {
        val intent = Intent().apply {
            type = "text/plain"
            action = Intent.ACTION_SEND
            putExtra(Intent.EXTRA_TEXT, details)
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        intent.resolveActivity(context.packageManager)?.let {
            context.startActivity(intent)
        }
    }
}

actual fun Module.factorySystemService(): KoinDefinition<SystemService> {
    return factoryOf(::AndroidSystemService)
}
Enter fullscreen mode Exit fullscreen mode

SystemService.android.kt

internal class IosSystemService : SystemService {

    override fun launchUrl(url: String) {
        if (url.isBlank()) return
        val nsUrl = NSURL(string = url)
        if (UIApplication.sharedApplication.canOpenURL(nsUrl)) {
            UIApplication.sharedApplication.openURL(nsUrl)
        }
    }

    override fun shareText(details: String) {
        val activityController = UIActivityViewController(
            activityItems = listOf(details),
            applicationActivities = null
        )
        val window = UIApplication.sharedApplication.windows().first() as UIWindow?
        activityController.popoverPresentationController()?.sourceView =
            window
        window?.rootViewController?.presentViewController(
            activityController as UIViewController,
            animated = true,
            completion = null
        )
    }
}

actual fun Module.factorySystemService(): KoinDefinition<SystemService> {
    return factoryOf(::IosSystemService)
}
Enter fullscreen mode Exit fullscreen mode

SystemService.ios.kt

We implement the functionality on each of the supported platforms, here Android and iOS, and provide an actual implementation for the Koin module that instantiates the SystemService implementation for each platform.

Usage

Use the factorySystemService() in a Koin module and inject SystemService. You will get the correct implementation for each platform to perform your actions.

Happy coding!

Top comments (0)