Going Swiftly: Using a Swift-only libraries in your Kotlin Multiplatform App
Tags: kotlin, kotlinmultiplatform, swift, gradle
Published: 2023-03-19 19:10:53

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](https://github.com/ttypic/swift-klib-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](https://github.com/ttypic/swift-klib-plugin/tree/main/examples/file-hasher).

## Table Of Contents

* [Getting Started](#getting-started)
* [Swift Klib](#swift-klib)
* [KMM Shared Module](#kmm-shared-module)
* [Using In SwiftUI](#using-in-swift)
* [Resources](#resources)


## Getting Started <a name="getting-started"></a>

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](https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html) 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](https://github.com/ttypic/swift-klib-plugin/tree/main/examples/file-hasher) 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:

```swift
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](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/#objc).

> 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](https://lazarevzubov.medium.com/compatible-with-objective-c-swift-code-e7c3239d949).

## Swift Klib <a name="swift-klib"></a>

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:

```kotlin
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
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:

```kotlin
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 <a name="kmm-shared-module"></a>

With the **Swift Klib** plugin properly configured, we can now access our wrapper for the _CryptoKit_ library in the **KMM** shared module:

```kotlin
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 <a name="using-in-swift"></a>

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:

```swift
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.

## Resources <a name="resources"></a>

1. [Swift Klib GitHub](https://github.com/ttypic/swift-klib-plugin)
2. [Compatible with Objective-C Swift Code](https://lazarevzubov.medium.com/compatible-with-objective-c-swift-code-e7c3239d949)
3. [Official KMM tutorial](https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html#next-step)
4. [How to use CryptoKit](https://medium.com/swlh/cryptokit-tutorial-how-to-use-cryptokit-on-ios13-apps-5961019752f5)