Ktor httpclient 身份验证功能无法在 IOS 上运行

Ktor httpclient auth feature not working on IOS

我正在开发一个 KMM 项目,身份验证在 Android 应用程序上运行良好。但是,当我在 httpclient(位于 shared.commonMain 中)中添加 Auth 功能时,ios 应用程序在运行时失败并显示以下消息

Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError. It is considered unexpected and unhandled instead. Program will be terminated.

这就是我创建 httpclient 的方式

private val httpclient = HttpClient() {
    engine {
        pipelining = true
        threadsCount = 4
    }
    install(Logging) {
        level = LogLevel.HEADERS
        logger = object : Logger {
            override fun log(message: String) {
                Napier.v(tag = "HTTP Client", message = message)
            }
        }
    }
    install(JsonFeature) {
        val json =  Json { ignoreUnknownKeys = true }
        serializer = KotlinxSerializer(json)
    }
    install(Auth) {
        basic {
            credentials {
                BasicAuthCredentials(username = emailUser, password = passwordUser)
            }
        }
    }
}.also {
    initLogger()
}

这是问候语的完整代码class:

class Greeting {
private var emailUser: String = ""
private var passwordUser: String = ""

private val httpclient = HttpClient() {
    engine {
        pipelining = true
        threadsCount = 4
    }
    install(Logging) {
        level = LogLevel.HEADERS
        logger = object : Logger {
            override fun log(message: String) {
                Napier.v(tag = "HTTP Client", message = message)
            }
        }
    }
    install(JsonFeature) {
        val json =  Json { ignoreUnknownKeys = true }
        serializer = KotlinxSerializer(json)
    }
    install(Auth) {
        basic {
            credentials {
                BasicAuthCredentials(username = emailUser, password = passwordUser)
            }
        }
    }
}.also {
    initLogger()
}

@Throws(Exception::class)
suspend fun getVaccines(): List<Vaccine> {
    return httpclient.get(endpointBase + Vaccine.path)
}

@Throws(Exception::class)
suspend fun loginUser(email: String, password: String): String? {
    emailUser = email
    passwordUser = password
    return httpclient.get(endpointBase + User.path + "/userPage")
}

}

异常完整堆栈跟踪

    Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen com.example.vaccinationcertificate_mobileapp.Greeting@3963788
    at 0   iosApp                              0x000000010c1f728f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
    at 1   iosApp                              0x000000010c1efbbd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93
    at 2   iosApp                              0x000000010c1efe2d kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93
    at 3   iosApp                              0x000000010c2272fd kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 93
    at 4   iosApp                              0x000000010c228aff ThrowInvalidMutabilityException + 431
    at 5   iosApp                              0x000000010c3db2c0 MutationCheck + 128
    at 6   iosApp                              0x000000010c151165 kfun:com.example.vaccinationcertificate_mobileapp.Greeting#<init>(){} + 437
    at 7   iosApp                              0x000000010c17c9de objc2kotlin.883 + 142
    at 8   iosApp                              0x000000010c150fa3 $sSo14SharedGreetingCABycfcTO + 19
    at 9   iosApp                              0x000000010c14f0af $sSo14SharedGreetingCABycfC + 31
    at 10  iosApp                              0x000000010c150596 $s6iosApp11ContentViewVACycfC + 38 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/ContentView.swift:4:0)
    at 11  iosApp                              0x000000010c14ebf3 $s6iosApp6iOSAppV4bodyQrvgAA11ContentViewVyXEfU_ + 35 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/iOSApp.swift:7:4)
    at 12  iosApp                              0x000000010c14eda0 $s6iosApp11ContentViewVIgo_ACIegr_TR + 16
    at 13  iosApp                              0x000000010c14edd1 $s6iosApp11ContentViewVIgo_ACIegr_TRTA + 17
    at 14  SwiftUI                             0x00000001173612cf $s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC + 63
    at 15  iosApp                              0x000000010c14eac5 $s6iosApp6iOSAppV4bodyQrvg + 181 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/iOSApp.swift:6:3)
    at 16  iosApp                              0x000000010c14ef79 $s6iosApp6iOSAppV7SwiftUI0B0AadEP4body4BodyQzvgTW + 9
    at 17  SwiftUI                             0x0000000116dce845 $s7SwiftUI15AppBodyAccessor33_A363922CEBDF47986D9772B903C8737ALLV06updateD02of7changedyx_SbtF0D0QzyXEfU_TA + 22
    at 18  SwiftUI                             0x0000000117357449 $s7SwiftUI12BodyAccessorPAAE03setC0yy0C0QzyXEFAFyXEfU_ + 34
    at 19  SwiftUI                             0x0000000116dce174 $s7SwiftUI15AppBodyAccessor33_A363922CEBDF47986D9772B903C8737ALLV06updateD02of7changedyx_SbtF + 1310
    at 20  SwiftUI                             0x00000001173575ac $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV11updateValueyyF + 161
    at 21  SwiftUI                             0x000000011754055c $s14AttributeGraph0A0VyACyxGqd__c5ValueQyd__RszAA12StatefulRuleRd__lufcADSPyqd__GXEfU_ySv_So11AGAttributeatcyXEfU_ySv_AJtcqd__mcfu_ySv_AJtcfu0_TA + 26
    at 22  AttributeGraph                      0x0000000110585e9b _ZN2AG5Graph11UpdateStack6updateEv + 553
    at 23  AttributeGraph                      0x0000000110586491 _ZN2AG5Graph16update_attributeENS_4data3ptrINS_4NodeEEEj + 411
    at 24  AttributeGraph                      0x000000011058c491 _ZN2AG5Graph20input_value_ref_slowENS_4data3ptrINS_4NodeEEENS_11AttributeIDEjPK15AGSwiftMetadataRhl + 299
    at 25  AttributeGraph                      0x00000001105a2889 AGGraphGetValue + 210
    at 26  SwiftUI                             0x00000001173574d5 $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV9container9ContainerQzvg + 67
    at 27  SwiftUI                             0x0000000117357599 $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV11updateValueyyF + 142
    at 28  SwiftUI                             0x000000011754055c $s14AttributeGraph0A0VyACyxGqd__c5ValueQyd__RszAA12StatefulRuleRd__lufcADSPyqd__GXEfU_ySv_So11AGAttributeatcyXEfU_ySv_AJtcqd__mcfu_ySv_AJtcfu0_TA + 26
    at 29  AttributeGraph                      0x0000000110585e9b _ZN2AG5Graph11UpdateStack6updateEv + 553

精确解:

  1. 在 commonMain.Platform
  2. 中移动了 emailUserpasswordUser
expect var emailUser: String  
expect var passwordUser: String
  1. androidMain.Platform
actual var emailUser = "" 
actual var passwordUser = ""
  1. iosMain.Platform
actual var emailUser: String = AtomicReference("").value
actual var passwordUser: String = AtomicReference("").value

你需要看看如何 kotlin-native concurrent-mutability works

简而言之,您不能在共享代码中使用任何 var,它们可能会被不同的线程访问。您必须用 Atomic 容器包装这些值。将 emailUserpasswordUser 替换为如下内容:

private val emailUser = Atomic("")
private val passwordUser = Atomic("")

也可以用delegated-properties这样就不用每次都写.value

公共代码没有原子声明,所以你必须自己做。实际上,对于 iOS 你可以使用原生原子,对于 android 只需做一个简单的包装。

好消息是这不会持续太久,因为 JetBrains 计划在 KMP 发布前不久更改并发模型。但现在我们不得不处理它。