BollywoodVillain
11/25/2020, 12:54 PMkotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen
. It works fine on Android and gets the network data without issues. I created a brand new MPP project using Android Studio MPP template and added the Ktor network code from Ktor-sample/client-mpp repo. What could be the problem?
The complete project is on Github at: https://github.com/samkhawase/Kotlin_MPP_Demo.
I’m using ktor 1.4.2
, Kotlin 1.4
, Kotlin plugin 1.4.20-release-Studio4.1.1
, and KMM plugin 0.2.0-release-65-Studio4.1
.
Here’s a snapshot of my sourceSets
from build.gradle.kt
(:shared)
val ktor_version = "1.4.2"
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
}
}
val commonTest by getting {
dependencies {
//...
implementation("io.ktor:ktor-client-core:$ktor_version")
}
}
val androidMain by getting {
dependencies {
//...
implementation("io.ktor:ktor-client-android:$ktor_version")
implementation("io.ktor:ktor-client-serialization-jvm:$ktor_version")
}
}
val androidTest by getting {
dependencies {
//...
implementation("io.ktor:ktor-client-android:$ktor_version")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
}
}
val iosTest by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
}
}
}
Here’s the failing Swift code
func getLocations() {
let apiService = ApiService()
apiService.about { (htmlString) in
print("🦋 htmlString:\n \(htmlString)")
}
}
struct ContentView: View {
var body: some View {
Text(greet()).onAppear {
print("isMainThread: \(Thread.isMainThread)")
getLocations()
}
}
}
Here’s the complete stacktrace from the iOS app.Cicero
11/25/2020, 12:56 PMisMainThread: true
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
Cicero
11/25/2020, 12:56 PMCicero
11/25/2020, 12:58 PM@Throws(Exception::class)
suspend fun getMeIOS(token: String): Me? {
return client.get<Me> {
url("https://")
accept(ContentType.Application.Json)
contentType(ContentType.Application.Json)
header(
"Authorization",
token
)
}
}
BollywoodVillain
11/25/2020, 12:58 PMCicero
11/25/2020, 12:58 PMBollywoodVillain
11/25/2020, 12:58 PMsuspend
in the ktor-samplesCicero
11/25/2020, 12:59 PMCicero
11/25/2020, 12:59 PMBollywoodVillain
11/25/2020, 12:59 PMpackage io.ktor.samples.mpp.client
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.coroutines.*
internal expect val ApplicationDispatcher: CoroutineDispatcher
class ApplicationApi {
private val client = HttpClient()
var address = Url("<https://tools.ietf.org/rfc/rfc1866.txt>")
fun about(callback: (String) -> Unit) {
GlobalScope.apply {
launch(ApplicationDispatcher) {
val result: String = client.get {
url(this@ApplicationApi.address.toString())
}
callback(result)
}
}
}
}
Cicero
11/25/2020, 1:00 PMCicero
11/25/2020, 1:00 PMBollywoodVillain
11/25/2020, 1:00 PMlet me give you a sample of how I unwrap this on my frontmuch appreciated! I was banging my head traversing the interwebz for this error
BollywoodVillain
11/25/2020, 1:01 PMAnd you believe them?Us newbies have no other choice 😅
Cicero
11/25/2020, 1:01 PMCicero
11/25/2020, 1:01 PMCicero
11/25/2020, 1:02 PMBollywoodVillain
11/25/2020, 1:05 PMApiService
class, not when I call the suspend func. Let me check onceBollywoodVillain
11/25/2020, 1:07 PMApiService
variable on Swift.Cicero
11/25/2020, 1:10 PMArkadii Ivanov
11/25/2020, 1:10 PMensureNeverFrozen()
into its init
section, so you will get a stack trace of the place, where it is actually gets frozen.Cicero
11/25/2020, 1:10 PMCicero
11/25/2020, 1:11 PMCicero
11/25/2020, 1:12 PMsourceSets {
val ktorVersion = "1.4.2"
val serializationVersion = "1.0.0-RC"
val coroutineVersion = "1.3.9-native-mt-2"
val commonMain by getting {
val commonCore = "io.ktor:ktor-client-core:$ktorVersion"
val commonJson = "io.ktor:ktor-client-json:$ktorVersion"
val commonSerialization = "io.ktor:ktor-client-serialization:$ktorVersion"
val commonSerializationCore = "org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion"
val common = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
dependencies {
implementation(commonCore)
implementation(commonSerializationCore)
implementation(commonJson)
implementation(commonSerialization)
implementation(common)
}
}
val androidMain by getting {
val ktorAndroid = "io.ktor:ktor-client-android:${ktorVersion}"
dependencies {
implementation(ktorAndroid)
}
}
val iosMain by getting{
val ktoriOS = "io.ktor:ktor-client-ios:${ktorVersion}"
dependencies {
implementation(ktoriOS)
}
}
}
Cicero
11/25/2020, 1:13 PMprivate val json = Json {
isLenient = true; ignoreUnknownKeys = true; coerceInputValues = true; useArrayPolymorphism =
true
}
internal val client = HttpClient() {
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
}
Arkadii Ivanov
11/25/2020, 1:13 PMjson
field, create the JSON directly in HttpClient lambdaCicero
11/25/2020, 1:14 PMBollywoodVillain
11/25/2020, 1:15 PMApiService.kt
is the culprit:
url(this@ApiService.address.toString())
Cicero
11/25/2020, 1:15 PMCicero
11/25/2020, 1:16 PMBollywoodVillain
11/25/2020, 1:16 PMBollywoodVillain
11/25/2020, 1:16 PMCicero
11/25/2020, 1:16 PMCicero
11/25/2020, 1:17 PMBollywoodVillain
11/25/2020, 1:17 PMCicero
11/25/2020, 1:17 PMArkadii Ivanov
11/25/2020, 1:19 PMArkadii Ivanov
11/25/2020, 1:19 PMBollywoodVillain
11/25/2020, 1:20 PMLooks like HttpClient lambda freezes the captured ApiService object. Try to avoid theThat’s what I’m trying. Thanksfield, create the JSON directly in HttpClient lambdajson
BollywoodVillain
11/25/2020, 1:21 PMYou can addstrangely android studio can’t findinto itsensureNeverFrozen()
sectioninit
ensureNeverFrozen()
reference when I do this:
class ApiService {
init {
ensureNeverFrozen()
}
//...
}
Arkadii Ivanov
11/25/2020, 1:21 PMBollywoodVillain
11/25/2020, 1:22 PMBollywoodVillain
11/25/2020, 1:23 PMBollywoodVillain
11/25/2020, 1:24 PMYou need expect/actual itDo you have any pointers on how to do this?
BollywoodVillain
11/25/2020, 1:25 PMexpect/actual
Arkadii Ivanov
11/25/2020, 1:25 PMArkadii Ivanov
11/25/2020, 1:26 PMBollywoodVillain
11/25/2020, 1:27 PMexpect/actual
is quite cool, much better than what I had to do to get C++ interop with iOS/Android.Arkadii Ivanov
11/25/2020, 1:29 PMInvalidMutabilityException
, it's shows you the place where the mutation attempt of a frozen object happened. If you you add ensureNeverFrozen
to that class, you will get another exception earlier, on freeze attempt.nrobi
11/25/2020, 1:29 PMpreventFreeze()
, which is essentially the same expect/actual
and uses ensureNeverFrozen
on the native partBollywoodVillain
11/25/2020, 1:30 PMCicero
11/25/2020, 1:33 PMMichal Klimczak
11/30/2020, 1:15 PMprivate val kotlinxSerializer = KotlinxSerializer(jsonConfig)
private val httpClient: HttpClient = HttpClient {
install(JsonFeature) {
serializer = kotlinxSerializer
}
}
If I remove serializer = kotlinxSerializer
, it doesn't crash.
(Also if I add preventFreeze
, the project doesn't compile at all with Command PhaseScriptExecution failed with a nonzero exit code
)
I have all ktor dependencies on 1.4.2
and corotuines at 1.4.2-native-mt
.Michal Klimczak
11/30/2020, 2:23 PMval ktorVersion = "1.4.1" //"1.4.2"
val coroutineVersion = "1.3.9-native-mt-2" //"1.4.2-native-mt-2"
val kotlinxSerialization = "1.0.0-RC2" //"1.0.1"
BollywoodVillain
12/07/2020, 12:06 PMKotlinXSerializer
init(). You can find the final working source code here:
https://github.com/samkhawase/Kotlin_MPP_DemoMichal Klimczak
12/07/2020, 12:09 PMBollywoodVillain
12/07/2020, 12:30 PMKotlinxSerializer
inside the install(JsonFeatue){
block. You can see my ApiSErvice.kt
file for reference.Michal Klimczak
12/07/2020, 1:20 PMMichal Klimczak
12/07/2020, 1:22 PMMichal Klimczak
12/07/2020, 1:23 PM