KMM and CI/CD

Amrit Pandey
Cox Auto Tech Blog
Published in
4 min readJul 29, 2021

In this topic we will go through about CI and CD part of KMM. CI /CD or DevOps is an important topic for hassle free development and distribution of binaries or product to its user.

CI/CD flow

The shared module or common code or business logic written in Kotlin has unit test cases to test and ensure business logic is working as expected on every contribution to the project. In KMM we generate binaries for consumption in both Android and iOS. As soon as the CI pipeline is satisfied with new changes, it will create an .aar for Android and .xcframework for iOS (containing framework for both simulator and device architecture). Then, the CD will distribute them to respective artifact repos. For CI/CD, our mobile DevOps teams at Cox Automotive use Azure pipelines, so we will be frequently using term Azure in our blog.

Let’s start with generation part then we will go to the distribution part:

Generation of binary

iOS

In iOS, XCFramework is the latest binary file available for distribution. KMM common code when compiled provides a .framework file for consumption in iOS. This .framework file is compiled for the current architecture mentioned in the Gradle file. We need to compile KMM code into two .framework files, one for the simulator(x86) and another for the device(arm64). Luckily in our case, we only support 64bit devices so we just need to have a couple of 64-bit .framework files. These frameworks ultimately get combined into one .xcframework file.

For generating KMM XCFramework in iOS, we used these commands:

  • packForXcode(Generated task in gradle): used to archive framework for simulator and device.
kotlin {   ios {      binaries {         framework {            baseName = "{frameworkname}"         }      }   }}val packForXcode by tasks.creating(Sync::class) {   group = "build"   val mode = System.getenv("CONFIGURATION") ?: "DEBUG"   val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"   val targetName = "ios" + if (sdkName.startsWith("iphoneos"))      "Arm64" else "X64"   val framework = kotlin.targets.getByName<KotlinNativeTarget> (targetName).binaries.getFramework(mode)   inputs.property("mode", mode)   dependsOn(framework.linkTask)   val targetDir = File(buildDir, "xcode-frameworks")   from({ framework.outputDirectory })   into(targetDir)}
  • xcodebuild -create-xcframework …: Create a universal xcFamework from device and simulator frameworks.

Android

In Android for generating .aar file we use below command:

  • assembleRelease: It is a defined gradle command for assembling all release builds.

Done.

Distribution

We have covered binary generation for both platform. Let’s move to the distribution part.

iOS

For distribution we have several options like cocoapod, carthage, SPM. We have opted out for SPM because adding cocoapod and carthage dependencies on cloud is a bulkier task. We need to install depended tools like homebrew, cocoapod everytime pipeline runs.

SPM is the latest dependency manager option given by Apple. It just requires the URL path of hosted dependency. It doesn’t require additional tools like cocoapod and carthage does.

Below are different stages of Azure pipeline to move generated XCFramework to SPM artifact repo:

  • Prepare release files: This task on azure pipeline copies build artifacts to a staging folder.
  • PublishBuildArtifacts: This task publishes XCFramework to Azure pipeline or a Windows file share.
  • DownloadArtifactRepo: Use this task to download SPM build artifacts.
  • CopyFiles: Copy files from a source folder to a target folder
  • Adding XCFramework to artifact repo : Deploy package.swift and .xcframework file to artifact repo, add tag(framework version) to commit.

Android

For Android since we have an .aar file, we will use a maven repository to host the binary files. Here at Cox Auto, we use Artifactory to distribute our binaries and consume them as any other external dependency. Here’s a guide on publishing to Artifactory.

Conclusion

While iOS uses a .framework file from KMM, so we convert that to XCFramework. Though in Android we can directly reference the KMM code as a dependency. That said, distributing with your code is generally not a good idea, so we opted to use an .aar file instead.

Though there are number of possible ways to distribute dependencies in both platforms, here at Cox Auto, we used Azure pipelines in combination with SPM and Maven repository.

So we are done with generating binaries and integrating them with the respective platforms. In next article, we will go through the consumption of XCFrameworks in iOS. Using KMM in Android does not pose a specific challenge since both compile to the same byte code. On the other hand, KMM gets transposed to Objective-C and certain features of Kotlin gets lost in translation.

Because of this reason, when using KMM in the iOS platform so we have to be careful and intentional. That’s what we will focus on in our next post.

DISCLOSURE STATEMENT: Opinions are those of the individual author. Unless noted otherwise in this post, Cox Automotive is not affiliated with, nor endorsed by, any of the companies mentioned. All trademarks and other intellectual property used or displayed are property of their respective owners.

--

--