PAYATHON 2026

KMP 中 Firebase Auth 的 await 和 user 为何无法解析?

数据小许

需要修正两处:先对 signInWithEmailAndPassword() 返回的 Task 调用 .await(),再读取 .user;同时确保 kotlinx-coroutines-play-services 已进入 :data 模块的 Android 编译依赖,并导入 kotlinx.coroutines.tasks.await。

原代码把 Task 当成了登录结果,存在类型使用错误。至于导入为何不可用,单凭当前片段还无法确定,需要检查依赖解析和编译日志。

为什么 .user 无法解析?

signInWithEmailAndPassword() 返回的是 Task<AuthResult>。原代码中的变量虽然叫 authResult,实际类型仍是 Task,因此不能直接读取 AuthResult.user。Firebase 的返回类型见 FirebaseAuth API 文档。

应改为:

val authResult = auth.signInWithEmailAndPassword(email, password).await()
val user = authResult.user

这里的 .await() 等待任务完成并返回其中的 AuthResult,等待期间不会阻塞线程。该扩展函数由 kotlinx-coroutines-play-services 提供,位于 kotlinx.coroutines.tasks 包中,详见 Task.await API 文档。

如何配置 androidMain 的依赖?

题目将依赖声明在 androidMain 中的方向是正确的。Android 专用依赖应声明在使用它的 KMP 模块的对应源集中,参见 KMP Android 依赖配置说明。

下面保留题目中的依赖版本,说明配置位置;使用前仍需确认它们与实际 Kotlin 编译器兼容:

kotlin {
    androidTarget()

    sourceSets {
        val androidMain by getting {
            dependencies {
                implementation(
                    project.dependencies.platform(
                        "com.google.firebase:firebase-bom:33.10.0"
                    )
                )
                implementation("com.google.firebase:firebase-auth")
                implementation(
                    "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.2"
                )
            }
        }
    }
}

project.dependencies.platform(...) 显式使用 Gradle 的 platform API。如果原来的 platform(...) 已能正常解析,无须仅为解决这个错误改写它。

随后在 FirebaseAuthDataSource.kt 中添加:

import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.tasks.await

kotlinx-coroutines-android 提供 Android 协程支持,但不能代替 Firebase Task 所需的 kotlinx-coroutines-play-services。后者提供 Task.await(),参见 协程 Play Services 集成模块。Firebase BoM 也不会管理 org.jetbrains.kotlinx 的协程版本。

完整的登录实现

以下代码沿用项目已有的 AuthRemoteRepository 和 UserToken 定义:

import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.tasks.await

class FirebaseAuthDataSource(
    private val auth: FirebaseAuth
) : AuthRemoteRepository {

    override suspend fun login(
        email: String,
        password: String
    ): Result<UserToken> {
        return try {
            val authResult = auth
                .signInWithEmailAndPassword(email, password)
                .await()

            val user = authResult.user
                ?: throw IllegalStateException("User is null")

            val tokenResult = user.getIdToken(true).await()
            val token = tokenResult.token
                ?: throw IllegalStateException("ID token is null")

            Result.success(UserToken(token))
        } catch (e: CancellationException) {
            throw e
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

两次 .await() 分别取得登录结果和 ID token 结果。示例在 token 为 null 时返回失败,避免通过 Result.success(UserToken("")) 返回缺失的凭据。

await() 支持协程取消,因此应先重新抛出 CancellationException,避免它被通用的 catch (e: Exception) 转换为普通业务失败。

导入仍然不存在时,按什么顺序排查?

  1. 确认文件属于正确源集。实现文件应位于 :data 模块的 src/androidMain/kotlin/ 下。仅放在其他模块的 androidMain,或实际位于 commonMain,都不能据此获得当前模块的 Android 专用依赖。
  2. 检查 Gradle Sync 的首个错误。确认依赖仓库包含 google() 和 mavenCentral(),并查看是否存在下载失败、仓库访问失败或依赖解析失败。
  3. 确认依赖进入 Android 编译配置。在项目根目录执行下面的命令,检查 Android 编译相关配置中是否包含 kotlinx-coroutines-play-services,以及最终解析到的版本:
./gradlew :data:dependencies
  1. 核对实际 Kotlin 版本。题目中的 2.0.0(或实际版本) 不能作为最终版本依据,应检查项目插件配置或版本目录。kotlinx.coroutines 从 1.10.0 起将构建所用 Kotlin 升级到 2.1.0,见 1.10.0 发布说明。如果日志出现 Kotlin metadata 版本不兼容,应据此调整 Kotlin 工具链或选择兼容的协程版本,并检查其他协程模块是否被依赖解析提升了版本。不能只凭 Unresolved reference 就认定一定是版本冲突。
  2. 依赖和编译恢复正常后,再处理 IDE 索引。若命令行编译正常而 IDE 仍标红,再重新同步或重建索引。依赖尚未解析成功时,反复清缓存通常无法解决根因。

不需要为解决这个错误把 Android Firebase SDK 移入 commonMain。共享接口可以保留在公共代码中,Firebase 实现继续放在 androidMain。

备注:内容仅供参考。