I only recently came across the capability to create an executable Swift package. Such a package can in turn depend on other Swift packages and, as such, I thought I’d see if I could use this approach to create a Swift command line app that consumes a package that contains Kotlin Multiplatform (KMP) code (and specifically the KMP code from PeopleInSpace).

Creating the initial executable Swift Package

All that’s needed to create the initial executable Swift package is to run the following.

 $ mkdir PeopleInSpace
 $ cd PeopleInSpace
 $ swift package init --type executable

This creates a basic initial Package.swift and main.swift. At this point you can build/run this using the following commands.

 $ swift build
 $ swift run
 Hello world


Consuming Kotlin Multiplatform package

The next step is to add a dependency to the Swift package containing the PeopleInSpace KMP code. We can do this by adding a package dependency in our Package.swift (we also need to add one to KMP-NativeCoroutines as this will allow us to use Swift async code to consume suspend functions and flows in the KMP shared code. We also add the related product dependencies to the executableTarget target.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "PeopleInSpace",
    platforms: [.macOS(.v13)],
    dependencies: [
        .package(url: "https://github.com/rickclephas/KMP-NativeCoroutines.git", exact: "1.0.0-ALPHA-18"),
        .package(url: "https://github.com/joreilly/PeopleInSpacePackage", branch: "main"),
    ],
    targets: [
        .executableTarget(
        name: "PeopleInSpace",
            dependencies: [
                .product(name: "KMPNativeCoroutinesAsync", package: "KMP-NativeCoroutines"),
                .product(name: "PeopleInSpaceKit", package: "PeopleInSpacePackage")
            ]
        )
    ]
)

Having done that we can now update our main.swift to invoke logic in the PeopleInSpace package. In this case we’re getting the list of people in space (using async/await and then periodically showing the position of the International Space Station (consumed as AsyncSequence)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import KMPNativeCoroutinesAsync
import PeopleInSpaceKit

KoinKt.doInitKoin()
let repository = PeopleInSpaceRepository()
let people = try await asyncFunction(for: repository.fetchPeople())
people.forEach { person in
    print(person.name)
}

let issPositionStream = asyncSequence(for: repository.pollISSPosition())
for try await issPosition in issPositionStream {
    print(issPosition)
}

At this point we can use swift run again to execute this and can also create an executable that we can run directly on the command line.

 $ swift build -c release
 $ .build/release/PeopleInSpace


Featured in Kotlin Weekly Issue #378