This article demonstrates how to use the Apple CryptoKit in KMM shared module. You'll learn how to create a Kotlin-compatible wrapper in Swift, build it using Swift Klib Gradle Plugin, and provide bindings in KMM shared module through Kotlin/Native cinterop
.
By using this technique, developers can access cryptographic algorithms (such as SHA, MD5, AES, ChaChaPoly, and more) in Kotlin Multiplatform without needing to rely on additional libraries. This is particularly beneficial since there are currently no widely-used and reliable cryptographic libraries for Kotlin Multiplatform from verified and respected companies.
Kotlin Multiplatform Mobile (KMM) is an SDK designed to simplify the development of cross-platform mobile applications. You can share common code between iOS and Android apps and write platform-specific code only where it's necessary
Kotlin Multiplatform brings a lot of benefits to the project, including increased code reuse, consistent business logic, and faster time-to-market. Additionally, one of its key advantages is the ability to access native APIs for each platform, enabling developers to tap into platform-specific functionality while still maintaining a shared codebase. Unfortunately, this powerful feature has certain limitations for the iOS platform. Kotlin does not have direct interoperability with the Swift language; instead, it relies on interoperability with Objective-C. As a result, there is no built-in support in Kotlin for Swift-only libraries that do not have an Objective-C API. For example, the Apple CryptoKit library cannot be directly accessed from Kotlin. Fortunately, a special Swift Klib Gradle Plugin exists to help overcome this limitation.
In this article, we'll explore how to use this plugin to build a simple Kotlin Multiplatform app that showcases the MD5 hash of a selected file.
The final code of the app can be found in the
/examples
folder of Swift Klib repository.
Table Of Contents
Getting Started
To get started, we'll create a Kotlin Multiplatform app using the KMM plugin in Android Studio.
If you run into any issues while creating your Kotlin Multiplatform app, be sure to check out the official KMM tutorial for additional guidance and support.
While this article will focus on the iOS implementation, if you're interested in exploring the Android implementation, you can check out our GitHub repository for more information.
Our first step will be to create a wrapper for the CryptoKit library that provides an Objective-C API for the MD5 hash function:
import CryptoKit
import Foundation
@objc public class KCrypto : NSObject {
@objc(md5:) public class func md5(data: NSData) -> String {
let hashed = Insecure.MD5.hash(data: data)
return hashed.compactMap { String(format: "%02x", $0) }.joined()
}
}
By utilizing the @objc
attribute, we can control which Objective-C API is generated from Swift code and how it will appear in Kotlin. You can find more information about this in the documentation.
It’s important that our class inherited from
NSObject
and has@objc
declarations, that make our Swift code compatible with Objective-C. More about Swift → Objective-C compatibility you can read in the article.
Swift Klib
With the code for our wrapper in hand, we can now proceed to add it to the shared module, which is located in the native/KCrypto
directory. Once we've done that, we're ready to set up the Swift Klib Gradle plugin. To add the plugin, simply include it in the plugins
section of the shared module's build.gradle.kts
file. Here's what the section should look like after adding the plugin:
plugins {
kotlin("multiplatform")
id("io.github.ttypic.swiftklib") version "0.2.1"
//...
}
After adding the Swift Klib plugin, the next step is to add cinterop
for the target platforms in the Kotlin Multiplatform Plugin. The good news is that there's no need to configure it or add a .def
file, as all the necessary configuration will be handled automatically by the Swift Klib plugin.
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64(),
).forEach {
it.compilations {
val main by getting {
cinterops {
create("KCrypto")
}
}
}
}
//...
}
Next, we need to provide the necessary settings for the Swift Klib plugin in the swiftklib
extension. This involves specifying the path
and packageName
parameters, which will be used by the plugin to generate the necessary bindings.
Here's an example of what the extension configuration might look like:
swiftklib {
create("KCrypto") {
path = file("native/KCrypto")
packageName("com.ttypic.objclibs.kcrypto")
}
}
In this case, we've specified that the Swift library files are located in the native/KCrypto
directory and that the generated package name should be com.ttypic.objclibs.kcrypto
. Be sure to adjust these settings to match the specifics of your project.
KMM Shared Module
With the Swift Klib plugin properly configured, we can now access our wrapper for the CryptoKit library in the KMM shared module:
import com.ttypic.objclibs.kcrypto.KCrypto
object FileMd5Hasher {
fun hash(nsdata: NSData): String {
return KCrypto.md5(data = nsdata)
}
}
Now we can use this FileMd5Hasher
both in KMM shared module and our iOS app module.
Using in SwiftUI
The final step is to use our FileMd5Hasher
to calculate the MD5 hash of a selected file in a SwiftUI app. Here's an example of how we might use FileMd5Hasher
to achieve this:
struct ContentView: View {
@State var fileMd5 = ""
@State var fileImporterVisible = false
var body: some View {
VStack(spacing: 10) {
if fileMd5 != "" { Text("File's MD5 hash:").font(.title) }
if fileMd5 != "" { Text(fileMd5) }
Button(action: {
fileImporterVisible.toggle()
}) {
Text("Choose File")
}
}.fileImporter(
isPresented: $fileImporterVisible,
allowedContentTypes: [.pdf],
allowsMultipleSelection: false
) { result in
do {
guard let selectedFile: URL = try result.get().first else { return }
guard let data = try? Data(contentsOf: selectedFile) else { return }
fileMd5 = FileMd5Hasher.shared.hash(nsdata: data)
} catch {
// Handle errors
}
}
}
}
While Kotlin has no direct interoperability with Swift, the Swift Klib Gradle plugin provides a solution for accessing Swift-only APIs in Kotlin Multiplatform shared modules. In this article, we explored how to use the Swift Klib plugin to create a wrapper for the CryptoKit library and calculate the MD5 hash of a selected file in a KMM app.
By following the steps outlined in this article, developers can easily integrate Swift-only APIs into their Kotlin Multiplatform projects, and take full advantage of the unique features and functionality provided by both languages.
Top comments (4)
Can the plugin be used with third party libraries distributed via CocoaPods? In your example, CryptoKit is Apple's built-in framework. But what if I need access to third party libraries (such as Kronos for example).
Unfortunately it is not possible for now, plugin only works with Apple's built-in frameworks. But I am thinking about ability to add dependencies. Maybe I'll add this functionality later, I am still thinking about right design for it. Stay tuned, I will post about new features.
Also if you need specifically Kronos, I guess as workaround you can connect Kronos source code as git submodule (or just copy source code to your repository) add Objective-C adapter and use plugin to build it. If you face any troubles, I will be happy to assist.
Please check it is possible to do with .def and Objective-C API defined in the header.
medium.com/kodein-koders/create-a-...