Most of the focus of Kotlin Multiplatform has, understandably, been on targeting the development of apps that run on Android and iOS. However, other platforms are supported so when I saw following announcement of SQLDelight’s support for macOS I was curious to see what would be involved in developing a basic app on that platform that uses same shared Kotlin code used in PeopleInSpace (A little bit more background on initial effort working with Android and iOS in following post Minimal Kotlin Multiplatform project using Compose and SwiftUI)

The Android, iOS and watchOS apps in PeopleInSpace had just displayed a list of astronauts currently in space but, given
additional real estate at our disposal on macOS, I thought I should expand to include other information available from People In Space API endpoint so decided to also show current position of ISS (International Space Station)

People In Space macOS

Updated Ktor client code

There isn’t anything macOS specific here but did add use of API that returns ISS position.

data class AstroResult(val message: String, val number: Int, val people: List<Assignment>)

data class Assignment(val craft: String, val name: String)

data class IssPosition(val latitude: Double, val longitude: Double)

data class IssResponse(val message: String, val iss_position: IssPosition, val timestamp: Long)

class PeopleInSpaceApi {
    private val baseUrl = ""

    private val client by lazy {
        HttpClient() {
            install(JsonFeature) {
                serializer = KotlinxSerializer(Json(JsonConfiguration(isLenient = true, ignoreUnknownKeys = true)))

    suspend fun fetchPeople() = client.get<AstroResult>("$baseUrl/astros.json")
    suspend fun fetchISSPosition() = client.get<IssResponse>("$baseUrl/iss-now.json")

macOS SwiftUI code

The SwiftUI code used is similar to that used for iOS and watchOS but now includes use of a Map View to show ISS position (along with associated update to View Model). This is using Combine’s Timer.publish() to periodically poll for updated position. Another approach might be to do the polling in shared Kotlin Code (perhaps an exercise for someone if they want to create PR for this :) )

struct ContentView: View {
    @ObservedObject var peopleInSpaceViewModel = PeopleInSpaceViewModel(repository: PeopleInSpaceRepository())
    let timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect()

    var body: some View {

        NavigationView {
            List(peopleInSpaceViewModel.people, id: \.name) { person in
                PersonView(person: person)
            .onReceive(timer) { _ in
            .onAppear(perform: {
            MapView(coordinate: CLLocationCoordinate2DMake(peopleInSpaceViewModel.issPosition.latitude, peopleInSpaceViewModel.issPosition.longitude))

struct PersonView : View {
    var person: Assignment
    var body: some View {
        Text( + " (" + person.craft + ")")

Gradle updates

These are the main updates to build.gradle for shared code module.

    fromPreset(presets.macosX64, 'macOS') {
        binaries {

    macOSMain.dependencies {
        // Coroutines
        implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-macosx64:${Versions.kotlinCoroutines}"

        // Ktor
        implementation "io.ktor:ktor-client-curl:${Versions.ktor}"
        implementation "io.ktor:ktor-client-core-macosx64:${Versions.ktor}"
        implementation "io.ktor:ktor-client-json-macosx64:${Versions.ktor}"
        implementation "io.ktor:ktor-client-logging-macosx64:${Versions.ktor}"
        implementation "io.ktor:ktor-client-serialization-macosx64:${Versions.ktor}"

        // Serialize
        implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-macosx64:${Versions.kotlinxSerialization}"

        // SQL Delight
        implementation "com.squareup.sqldelight:native-driver-macosx64:${Versions.sqlDelight}"
        implementation "com.squareup.sqldelight:runtime-macosx64:${Versions.sqlDelight}"


This is currently using 1.3.5-native-mt version of Kotlin Coroutines. Ktor does not currently fully work with this version so, for now this is using runBlocking workaround when invoking API requests - more info in this Ktor issue

Featured in Kotlin Weekly Issue #194