Skip to content

Instantly share code, notes, and snippets.

@epool
Last active December 5, 2023 13:11
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save epool/b55f9b8f64427efc5717510e0246c278 to your computer and use it in GitHub Desktop.
Save epool/b55f9b8f64427efc5717510e0246c278 to your computer and use it in GitHub Desktop.
Builds a XcFramework from a shared module in KMM.
//region XcFramework tasks
val xcFrameworkPath = "xcframework/${project.name}.xcframework"
tasks.create<Delete>("deleteXcFramework") { delete = setOf(xcFrameworkPath) }
val buildXcFramework by tasks.registering {
dependsOn("deleteXcFramework")
group = "build"
val mode = "Release"
val frameworks = arrayOf("iosArm64", "iosX64")
.map { kotlin.targets.getByName<KotlinNativeTarget>(it).binaries.getFramework(mode) }
inputs.property("mode", mode)
dependsOn(frameworks.map { it.linkTask })
doLast { buildXcFramework(frameworks) }
}
fun Task.buildXcFramework(frameworks: List<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>) {
val buildArgs: () -> List<String> = {
val arguments = mutableListOf("-create-xcframework")
frameworks.forEach {
arguments += "-framework"
arguments += "${it.outputDirectory}/${project.name}.framework"
}
arguments += "-output"
arguments += xcFrameworkPath
arguments
}
exec {
executable = "xcodebuild"
args = buildArgs()
}
}
//endregion
@StefanOltmann
Copy link

Very helpful! Thank you a lot. :)

I use this to combine iOS and macOS for SwiftUI Multiplatform.

@handstandsam
Copy link

handstandsam commented Jun 12, 2021

Thanks, this is awesome! My framework is a different name than the ${project.name}. I ended up grabbing the framework.baseName in order to make this dynamic and work in that case.

val xcFrameworkPath = "xcframework/${project.kotlin.iosArm64().binaries.getFramework(mode).baseName}.xcframework"
and
arguments += "${it.outputDirectory}/${it.baseName}.framework"

val mode = "Release"
val xcFrameworkPath = "xcframework/${project.kotlin.iosArm64().binaries.getFramework(mode).baseName}.xcframework"
println("xcFrameworkPath $xcFrameworkPath")
tasks.create<Delete>("deleteXcFramework") {
    delete = setOf(xcFrameworkPath)
}

val buildXcFramework by tasks.registering {
    dependsOn("deleteXcFramework")
    group = "build"

    val frameworks = arrayOf("iosArm64", "iosX64")
        .map { kotlin.targets.getByName<KotlinNativeTarget>(it).binaries.getFramework(mode) }
    inputs.property("mode", mode)
    dependsOn(frameworks.map { it.linkTask })
    doLast { buildXcFramework(frameworks) }
}

fun Task.buildXcFramework(frameworks: List<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>) {
    val buildArgs: () -> List<String> = {
        val arguments = mutableListOf("-create-xcframework")
        frameworks.forEach {
            arguments += "-framework"
            arguments += "${it.outputDirectory}/${it.baseName}.framework"
        }
        arguments += "-output"
        arguments += xcFrameworkPath
        arguments
    }
    exec {
        executable = "xcodebuild"
        args = buildArgs()
    }
}

Is there a cleaner way to do this? It works. I have a framework name that is different because my module name in Gradle is client-side and the framework name ends up being client_side.

@LRH-iOS
Copy link

LRH-iOS commented Oct 26, 2023

how to use BuildXcFramework.kt?

@rzolin
Copy link

rzolin commented Dec 5, 2023

Right, I would love to know how to use the script above.

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