Skip to content

Instantly share code, notes, and snippets.

@LouisCAD
Forked from Ribesg/!README.md
Created February 28, 2019 11:31
Show Gist options
  • Save LouisCAD/3a9d8fc1f8dc118b9ae5fb8f98abe118 to your computer and use it in GitHub Desktop.
Save LouisCAD/3a9d8fc1f8dc118b9ae5fb8f98abe118 to your computer and use it in GitHub Desktop.
How to use an iOS Framework in a Kotlin MPP library published to Maven

This gist demonstrates how to build a Kotlin MPP library so that the iOS sourceSet depends on and uses an iOS Framework as a dependency.

Key ideas:

  • We use Carthage to retrieve/build the iOS Framework.
  • We use cinterop to create bindings allowing us to use the iOS Framework from Kotlin
  • We build and publish the library using ./gradlew publishToMavenLocal
  • We build and publish klib artifacts for both the arm64 and x86_64 architectures, you can easily add arm32 if you need.
  • Note that the publish process also publishes a cinterop klib artifact, allowing dependents to also know about the iOS Framework headers.

You can find a gist explaining how to use such library in an iOS app here.

More notes:

  • The .def file's name contains its path with / replaced with -.
  • I have no idea what I'm doing in this .def file, but it works. Half the entries are probably useless.
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
group = "com.example.lib"
version = "1.2.3"
repositories {
google()
jcenter()
}
plugins {
id("maven-publish")
id("com.android.library") version "3.2.1"
id("org.jetbrains.kotlin.multiplatform") version "1.3.21"
}
android {
compileSdkVersion(28)
defaultConfig {
minSdkVersion(16)
targetSdkVersion(28)
}
sourceSets {
val main by getting {
setRoot("src/androidMain")
}
val test by getting {
setRoot("src/androidTest")
}
}
lintOptions {
isAbortOnError = false
}
packagingOptions {
exclude("META-INF/*.kotlin_module")
}
}
kotlin {
data class IosTarget(val name: String, val preset: String, val id: String)
val iosTargets = listOf(
IosTarget("ios", "iosArm64", "ios-arm64"),
IosTarget("iosSim", "iosX64", "ios-x64")
)
android {
publishAllLibraryVariants()
}
for ((targetName, presetName, id) in iosTargets) {
targetFromPreset(presets.getByName<KotlinNativeTargetPreset>(presetName), targetName) {
compilations {
val main by getting {
val carthageBuildDir = "$projectDir/Carthage/Build/iOS"
cinterops(Action {
val bugsnag by creating {
defFile("src/iosMain/cinterop/bugsnag.def")
includeDirs.allHeaders("$carthageBuildDir/Bugsnag.framework/Headers")
}
})
linkerOpts("-F$carthageBuildDir")
}
}
mavenPublication {
artifactId = "${project.name}-$id"
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val androidMain by getting {
dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
implementation("com.bugsnag:bugsnag-android:4.11.0")
}
}
val iosMain by getting {
}
val iosSimMain by getting {
dependsOn(iosMain)
}
}
}
// Create Carthage tasks
listOf("bootstrap", "update").forEach { type ->
task<Exec>("carthage${type.capitalize()}") {
group = "carthage"
executable = "carthage"
args(
type,
"--platform", "iOS",
"--no-use-binaries", // Provided binaries are sometimes problematic, remove this to speedup process
"--cache-builds"
)
}
}
// Make CInterop tasks depend on Carthage
tasks.withType(CInteropProcess::class) {
dependsOn("carthageBootstrap")
}
// Delete build directory on clean
tasks.named<Delete>("clean") {
delete(buildDir)
}
// Temporary workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations.create("compileClasspath")
github "bugsnag/bugsnag-cocoa" ~> 5.0
rootProject.name = "my-mpp-library"
enableFeaturePreview("GRADLE_METADATA")
pluginManagement {
repositories {
google()
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "com.android.library") {
useModule("com.android.tools.build:gradle:${requested.version}")
}
}
}
}
depends = Foundation
package = framework.Bugsnag
language = Objective-C
headers = Bugsnag.h
headersFilter = **
compilerOpts = -framework Bugsnag -fmodules
linkerOpts = -framework Bugsnag
@LouisCAD
Copy link
Author

LouisCAD commented Sep 5, 2019

This is a just a fork of another gist. All the credit goes to Gael Ribes. Please ask him in his gist. I haven't played much with CInterop yet to be helpful on that right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment