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.
This creates a basic initial Package.swift
and main.swift
. At this point you can build/run this using the following commands.
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.
Featured in Kotlin Weekly Issue #378
Related tweet
Wrote a very short article about creating a Swift command line app that consumes Kotlin Multiplatform code. Cool option to have! #KMP #Swift #Kotlinhttps://t.co/VdUstwXZNY
— John O'Reilly (@joreilly) October 27, 2023