You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KMM Library中如何在Swift中实现Kotlin密封类的Switch匹配

问题描述

我正在开发一个KMM库(而非KMM应用),会打包成.xcframework/.framework供Xcode项目使用,以及.aar供Android项目使用。

我需要实现UIState状态(Loading、Success、Error),于是创建了如下Kotlin密封类:

sealed class UIState<out T> {
    object Loading : UIState<Nothing>()
    data class Success<T>(val data: T) : UIState<T>()
    data class Error(val message: String) : UIState<Nothing>()
}

接着用Coroutine编写了API调用函数:

suspend fun getDotaHeroesWithSealedClass(): UIState<List<DotaHero>> =
        when (httpClient.get("https://api.opendota.com/api/heroes").status.value) {
            in 200..299 -> {
                UIState.Success(httpClient.get("https://api.opendota.com/api/heroes").body())
            }
            else -> {
                UIState.Error("An error has occured")
            }
        }

执行./gradlew assembleXCFramework命令生成.xcframework并关联到Xcode项目后,发现该函数的返回类型被识别为UIState,无法在Swift中像预期那样进行switch分支匹配:

switch uiState {
   case .loading:
   case .success(let data):
   case .error(let errorMessage):
}
解决方案

要让Kotlin密封类在Swift中正确识别并支持模式匹配,按以下步骤调整:

1. 给密封类添加序列化支持(可选但关键)

Kotlin/Native对带泛型的密封类的Swift映射需要序列化支持来确保类型信息正确传递。先在库的build.gradle.kts中添加序列化插件和依赖:

plugins {
    kotlin("multiplatform")
    kotlin("plugin.serialization") version "和你的Kotlin版本匹配"
}

sourceSets {
    commonMain.dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0")
    }
}

然后给密封类及内部类加上@Serializable注解:

import kotlinx.serialization.Serializable

@Serializable
sealed class UIState<out T> {
    @Serializable
    object Loading : UIState<Nothing>()
    @Serializable
    data class Success<T>(val data: T) : UIState<T>()
    @Serializable
    data class Error(val message: String) : UIState<Nothing>()
}

2. 优化API调用并修正泛型映射

原代码中重复发起了两次HTTP请求,先优化这一点;另外Kotlin的List默认映射为Swift的NSArray,要让它转为原生Array,确保泛型类型无歧义:

suspend fun getDotaHeroesWithSealedClass(): UIState<List<DotaHero>> {
    val response = httpClient.get("https://api.opendota.com/api/heroes")
    return when (response.status.value) {
        in 200..299 -> UIState.Success(response.body())
        else -> UIState.Error("An error has occurred")
    }
}

3. 配置Kotlin/Native的Swift互操作参数

在build.gradle.kts的Kotlin配置块中,添加Swift泛型支持的编译参数,确保密封类被正确生成为Swift可匹配的枚举结构:

kotlin {
    // 你的iOS目标配置
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        // 你的依赖配置
    }

    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.framework {
            baseName = "你的库名称"
            isStatic = true // 静态库配置,按需调整

            // 添加泛型支持编译参数
            freeCompilerArgs += "-Xobjc-generics"
        }
    }
}

4. 确保DotaHero类的兼容性

DotaHero数据类需要是可序列化的,避免使用Swift不兼容的特性(比如无默认值的参数、复杂嵌套泛型),这样才能在Swift中被识别为原生模型类型。

完成所有调整后,重新执行./gradlew assembleXCFramework生成框架。此时在Swift项目中,getDotaHeroesWithSealedClass的返回类型会正确识别为UIState<[DotaHero]>,并且可以使用你期望的switch分支匹配逻辑。

内容的提问来源于stack exchange,提问作者Mohammad Azri Khairuddin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 12:35:19