· 5 min read Posted by Kevin Schildhorn
Adding a Custom Artifact Manager
How to create a Custom Artifact Manager for KMMBridge
KMMBridge is a great gradle plugin that allows you to publish Xcode XCFrameworks from your Kotlin Multiplatform projects. It works by uploading XCFrameworks to an artifact repository and then configures your project to point to the uploaded framework. KMMBridge supports uploading to GitHub, Maven and Amazon S3 out of the box, but what if you want to store your frameworks somewhere else? Somewhere like Google Cloud Storage?
Well KMMBridge supports this by including an interface called ArtifactManager
that can be implemented and passed into KMMBridge. This interface tells KMMBridge how to upload your framework and where to look for it. This two part post will go over how to create a custom Artifact Manager that hosts your frameworks in Google Cloud Storage.
Writing an Artifact Manager
The KMMBridge Docs cover the anatomy of an ArtifactManager
, but it can still be confusing without know what you’re looking for.
class ExampleArtifactManager(
private val folder: String
) : ArtifactManager {
lateinit var specialToken: String
override fun configure(
project: Project,
version: String,
uploadTask: TaskProvider<Task>,
kmmPublishTask: TaskProvider<Task>
) {
this.specialToken = project.specialToken
}
override fun deployArtifact(
task: Task,
zipFilePath: File,
version: String
): String {
// Use specialToken here
return "https://touchlab.co/$folder/myframework-$version.zip"
}
}
Here you can see a barebones ArtifactManager
. You can see that there are two functions: configure
and deployArtifact
.
The first function configure
gives you a chance to configure your artifact using gradle properties and tasks. In this example we are getting a special token that would be used later in uploading.
The second function deployArtifact
should handle deployment. This of course isn’t a full example (we’ll go over this more in the second part), but notice how it uses the version passed in from configure and the folder variable passed in the constructor. The ArtifactManager
is constructed in your build.gradle.kts
file in the KMMBridge
block:
kmmbridge {
artifactManager.set(ExampleArtifactManager(folder = ""))
}
So now we know how to create an ArtifactManager
, but where should it go?
Where to put an Artifact Manager
There’s one very important detail when creating a custom ArtifactManager
that you need to know:
ArtifactManagers are referenced from gradle files (
gradle.kts
), not from regular kotlin files (.kt
).
The manager is used when building your project with gradle, which means you can’t just define it in your apps codebase and expect to be able to reference it. It needs to be defined in either a gradle plugin or in the build.gradle.kts
itself. Lets go over the two options and what they look like.
There is a third option as well, to add it to your buildSrc
. This should also be technically possible however I ran into many issues trying to get this to work so I wouldn’t recommend it personally.
Option 1. In the build.gradle
If you want a really simple approach to adding an ArtifactManager
, then you can take the quick and dirty approach and create the class in your build.gradle.kts
. This might look something like this:
// build.gradle.kts
plugins {
...
}
buildscript {
dependencies {
// Any dependencies for your ArtifactManager
classpath(...)
}
}
class YourArtifactManager : ArtifactManager {
override fun deployArtifact(task: Task, zipFilePath: File, version: String): String {
TODO("")
}
}
kmmbridge {
artifactManager.set(YourArtifactManager())
spm(swiftToolVersion = "5.8") {
iOS { v("14") }
}
}
Here you can see you’re creating the class YourArtifactManager
in the gradle file and then referencing it in the kmmbridge
block. While this is quick and simple there are some negatives with this approach:
- it’s messy to be defining classes in your gradle file
- It’s not reusable. If you have two modules or projects that need this manager then you need to define it each time
- If your
ArtifactManager
has dependencies then it will require including thebuildscript
block, which is not really recommended anymore. You can still use it but it’s but it’s considered legacy and has been replaced.
Option 2. As a Plugin
While it requires some additional work, adding your ArtifactManager
as a separate plugin is much cleaner, allows re-use and doesn’t use the buildscript
block. If you have multiple projects using your ArtifactManager
then you can create a separate project and publish your plugin to maven. If you’re only using it in one project you can create a new module for your ArtifactManager
. Here is an example of how you can do that.
First, create a new module. In your modules build.gradle.kts
add the following code
plugins {
`kotlin-dsl`
}
@Suppress("UnstableApiUsage")
gradlePlugin {
plugins {
register("your-plugin") {
id = "com.example.yourid"
implementationClass = "com.example.yourid.YourPlugin"
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("co.touchlab.kmmbridge:kmmbridge:1.2.0")
// Additional dependencies
}
Then add a file for your plugin
package com.example.yourid
import org.gradle.api.Plugin
import org.gradle.api.Project
@Suppress("unused")
class YourPlugin : Plugin<Project> {
override fun apply(target: Project) {}
}
This class is just so that gradle can find your plugin so there doesn’t need to be much there. On that note, you also need to tell gradle where to find your plugin. To do that, update your settings.gradle.kts
pluginManagement {
includeBuild("yourModuleName")
...
}
This is basically the plugin equivalent of implementation(project(":myModule"))
.
Do not include your plugin module in your settings.gradle.kts
file (i.e. include(":yourModuleName")
). The module is already included in the pluginManagement
and this will cause issues.
Finally add your ArtifactManager
in your new module. Add any dependencies needed and build your library. Assuming everything builds correctly, add the plugin to your app.
import com.example.yourid.YourArtifactManager
plugins {
...
id("com.example.yourid")
}
kmmbridge {
artifactManager.set(YourArtifactManager())
spm(swiftToolVersion = "5.8") {
iOS { v("14") }
}
}
Sync your project and everything should be ready to go! You don’t need to call a publish gradle function, it should be handled automatically.
Next Section : Publishing artifacts to Google Cloud Storage
Hopefully now you have a foundation with your plugin and ArtifactManager
, plus a good understanding of the Artifact Manager. In the next section, we’ll go over uploading to Google Cloud Storage using our new manager.
Comments
Reply on Bluesky here to join the conversation.
No comments yet. Be the first to comment!