Thread
#multiplatform
    c

    coletz

    3 years ago
    Hi guys. I'm going crazy with some code. I have 2 mpp, one I manually created like 2 months ago and one created with "new>project>kotlin mpp (ios/android)". The first one is working fine, while the second one is throwing an error on ios (
    kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen <object>@839b6268
    ). The whole code is the same. Anyone ever had similar issue?
    common code:
    internal expect val ApplicationDispatcher: CoroutineDispatcher
    
    class PokeApi {
    
        private val httpClient = HttpClient()
    
        fun getPokemonList(success: (PokemonList) -> Unit){
            GlobalScope.launch(ApplicationDispatcher){
                val json: String = httpClient.get {
                    url("<https://pokeapi.co/api/v2/pokemon/>")
                }
                val deferred = GlobalScope.async(ApplicationDispatcher) {
                    JSON.nonstrict.parse(PokemonList.serializer(), json)
                }
                success(deferred.await())
            }
        }
    }
    
    @Serializable
    data class Pokemon(
        val name: String,
        val url: String
    )
    
    @Serializable
    data class PokemonList(
        val count: Int,
        val results: List<Pokemon>
    )
    t

    thevery

    3 years ago
    Try to freeze all the lambdas, e.g. http client callback
    Recent KN releases are more strict on this, I've encountered similar issue
    p

    pandawarrior

    3 years ago
    Any solution for it yet? I believe it’s the same issue with mine on iOS
    mutation attempt of frozen <object>@c4226468
    private val client = HttpClient()
    
        fun callSimpleApi() {
            try {
                launch {
                    getToolString()
                }
            } catch (e: Exception) {
                sampleView.returnString(e.toString())
            }
        }
    
        suspend fun getToolString() = client.get<String> {
            url("<https://tools.ietf.org/rfc/rfc1866.txt>")
        }
    s

    Sabrina Namur

    3 years ago
    I'm not sure, because you said both projects have the same code, but I got this problem with kotlin 1.3.11. With kotlin 1.3.10 everything works fine.
    p

    pandawarrior

    3 years ago
    Oh really? let me check with kotlin 1.3.10.
    @Sabrina Namur You are right, 1.3.11 is the problem. Thanks!
    c

    coletz

    3 years ago
    Oh, the code was the same but I had different kotlin version... Thanks! But I'd like to know how to freeze lambda... Can you provide an example @thevery?
    t

    thevery

    3 years ago
    Sure. Non-frozen:
    listOf(1, 2, 3).filter { it: Int -> it % 2 == 0 }
    Frozen:
    listOf(1, 2, 3).filter({ it: Int -> it % 2 == 0 }.freeze())
    c

    coletz

    3 years ago
    Actually it's not possible to freeze on common code, right? One should call freeze on the iOS code, or I'm missing something?
    t

    thevery

    3 years ago
    I freeze on iOS side, in common you can create expect/actual or use https://github.com/touchlab/Stately
    c

    coletz

    3 years ago
    so stately is just defining empty expect for other platforms (eg android) in order to use freeze and other stuff on common code... Basically what I've just done, but I can't get how I should call .freeze() on the code I provided before. You said to freeze httpclient's lambda callback, but these are builders and not a lambda called on call completed. That's a coroutine so there's no callback. Sorry if this sound stupid 😅
    t

    thevery

    3 years ago
    ic. Can you try to log every line to get the actual crash reason?
    c

    coletz

    3 years ago
    yep!
    class PokeApi {
    
        private val httpClient = HttpClient()
    
        fun getPokemonList(success: (PokemonList) -> Unit){
            1.log()
            GlobalScope.launch(ApplicationDispatcher){
                2.log()
                val json: String = httpClient.get {
                    3.log()
                    url("<https://pokeapi.co/api/v2/pokemon/>")
                    4.log()
                }
                5.log()
                val deferred = GlobalScope.async(ApplicationDispatcher) {
                    6.log()
                    JSON.nonstrict.parse(PokemonList.serializer(), json)
                }
                7.log()
                success(deferred.await())
                8.log()
            }
        }
    }
    
    fun Int.log(){
        println("========= $this")
    }
    the crash is after log number 4
    t

    thevery

    3 years ago
    Looks like freeze is needed in ktor itself, @e5l do you know about this issue?
    e5l

    e5l

    3 years ago
    Yep, WIP. The fix would be available in the next minor release.
    p

    Patrick Jackson

    3 years ago
    Is this resolved now? Getting same exception with ktor 1.1.3 kotlin 1.3.21 coroutine 1.1.1
    e5l

    e5l

    3 years ago
    Nope, still [WIP] 😞. You could fix the exception by preventing
    client
    instance freeze(do not create it in global scope or
    object
    singleton)
    c

    coletz

    3 years ago
    So it seems like KT-30454 is by design and nothing is "really" broken... That means we will need to always instantiate a new client every time. From your knowledge is it possible to instantiate the client inside something like a view model and use it multiple times or this will still crash?
    e5l

    e5l

    3 years ago
    Yes, and we testing the fix.