Hi. Could you please elaborate what is getting fro...
# reaktive
a
Hi. Could you please elaborate what is getting frozen?
a
Updated snippet to point at place which causes crash.
a
So DataProcessor is frozen, right?
a
Stacktrace:
Copy code
Uncaught exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.UnsafeLazyImpl@ac9548kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.UnsafeLazyImpl@ac9548
    at 0   PF Keychain                         0x000000010a7b5fef kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
    at 1   PF Keychain                         0x000000010a7aeacd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93
    at 2   PF Keychain                         0x000000010a7aecad kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93
    at 3   PF Keychain                         0x000000010a7de70d kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 93
    at 4   PF Keychain                         0x000000010a7deccf ThrowInvalidMutabilityException + 431
    at 5   PF Keychain                         0x000000010a8d9a90 MutationCheck + 128
    at 6   PF Keychain                         0x000000010a7a2328 kfun:kotlin.UnsafeLazyImpl.<set-_value>#internal + 104
    at 7   PF Keychain                         0x000000010a7a25b4 kfun:kotlin.UnsafeLazyImpl#<get-value>(){}1:0 + 580
    at 8   PF Keychain                         0x000000010a6d76b8 kfun:com.printful.keychain.supporting.client.UserClient.<get-clientInfo>#internal + 328
    at 9   PF Keychain                         0x000000010a6d7c73 kfun:com.printful.keychain.supporting.client.UserClient#getAccessToken(kotlin.String){}com.badoo.reaktive.single.Single<com.printful.keychain.supporting.result.Result<com.printful.keychain.models.remote.OAuthResponse,kotlin.Throwable>> + 1315
    at 10  PF Keychain                         0x000000010a697ec8 kfun:com.printful.keychain.modules.home.stores.HomeStoreAbstractFactory.HomeBootstrapper.fetchAccessToken$lambda-1#internal + 376
    at 11  PF Keychain                         0x000000010a69866e kfun:com.printful.keychain.modules.home.stores.HomeStoreAbstractFactory.HomeBootstrapper.$fetchAccessToken$lambda-1$FUNCTION_REFERENCE$79.invoke#internal + 206
    at 12  PF Keychain                         0x000000010a92988f kfun:com.badoo.reaktive.single.object-22.onSuccess#internal + 415
    at 13  PF Keychain                         0x000000010a93eebd kfun:com.badoo.reaktive.single.object-37.onSuccess#internal + 541
    at 14  PF Keychain                         0x000000010a92c51a kfun:com.badoo.reaktive.single.object-26.onSuccess$lambda-0#internal + 138
    at 15  PF Keychain                         0x000000010a92c744 kfun:com.badoo.reaktive.single.object-26.$onSuccess$lambda-0$FUNCTION_REFERENCE$410.invoke#internal + 68
    at 16  PF Keychain                         0x000000010a92c7a0 kfun:com.badoo.reaktive.single.object-26.$onSuccess$lambda-0$FUNCTION_REFERENCE$410.$<bridge-UNN>invoke(){}#internal + 64
    at 17  PF Keychain                         0x000000010a933858 kfun:com.badoo.reaktive.scheduler.MainScheduler.ExecutorImpl.Operation.invoke#internal + 456
    at 18  PF Keychain                         0x000000010a933b10 kfun:com.badoo.reaktive.scheduler.MainScheduler.ExecutorImpl.Operation.$<bridge-UNN>invoke(){}#internal + 64
    at 19  PF Keychain                         0x000000010a934039 _636f6d2e6261646f6f2e7265616b746976653a7265616b74697665_knbridge7 + 185
    at 20  libdispatch.dylib                   0x000000010b7fa7ec _dispatch_call_block_and_release + 12
    at 21  libdispatch.dylib                   0x000000010b7fb9c8 _dispatch_client_callout + 8
    at 22  libdispatch.dylib                   0x000000010b809e75 _dispatch_main_queue_callback_4CF + 1152
    at 23  CoreFoundation                      0x00007fff2038fdbb __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    at 24  CoreFoundation                      0x00007fff2038a63e __CFRunLoopRun + 2685
    at 25  CoreFoundation                      0x00007fff203896d6 CFRunLoopRunSpecific + 567
    at 26  GraphicsServices                    0x00007fff2c257db3 GSEventRunModal + 139
    at 27  UIKitCore                           0x00007fff24696cf7 -[UIApplication _run] + 912
    at 28  UIKitCore                           0x00007fff2469bba8 UIApplicationMain + 101
    at 29  libswiftUIKit.dylib                 0x00007fff551885f2 $s5UIKit17UIApplicationMainys5Int32VAD_SpySpys4Int8VGGSgSSSgAJtF + 98
Looks like the lambda gets frozen and it is not possible to perform
Client.getAccessToken
.
a
It still not clear which object is being frozen. It it is the DataProcessor, then you can try to extract the code to a private function:
private fun freshAccessToken(client, storage): Observable
This should prevent
this
from being captured and frozen
But the Storage will be still frozen, I assume it is fine.
Is Storage mutable?
a
It is just a wrapper around SqlDelight database.
a
Then I suppose extraction should help
a
Storage.getAccessToken
executes sqldelight query and gets accessToken from db.
Client.getAccessToken
is just
expect/actual
Network client.
a
What about Client? Is it mutable?
a
Client is just a wrapper around URLSession in iOS.
a
Does it have any mutable properties?
a
Nope
a
Btw, don't forget to manually freeze the callback passed to the iOS queue
a
Copy code
internal actual class PlatformNetwork : Network, KoinComponent {
    private val urlSession: Any? by inject(qualifier = named(Deps.URL_SESSION))

    override fun makeRequest(request: Request) = when (request.method) {
        HttpMethod.GET -> makeGetRequest(request)
        <http://HttpMethod.POST|HttpMethod.POST> -> makePostRequest(request)
    }

    private fun makeGetRequest(request: Request) = single<Request.Response> { emitter ->
        val callback: URLSessionResult = emitter::handle
        val urlRequest = buildGetRequest(request)
        val urlSession = (urlSession as NSURLSession)
        val task = urlSession.dataTaskWithRequest(urlRequest, callback.freeze())
        task.resume()
        emitter.setDisposable(Disposable(task::cancel))
    }

    private fun makePostRequest(request: Request) = single<Request.Response> { emitter ->
        val callback: URLSessionResult = emitter::handle
        val urlRequest = buildPostRequest(request)
        val urlSession = (urlSession as NSURLSession)
        val task = urlSession.dataTaskWithRequest(urlRequest, callback.freeze())
        task.resume()
        emitter.setDisposable(Disposable(task::cancel))
    }

    private fun buildGetRequest(request: Request): NSMutableURLRequest {
        val urlRequest = NSMutableURLRequest()
        urlRequest.setHTTPMethod(HttpMethod.GET.name)
        urlRequest.buildURL(request)
        request.headers?.forEach { header ->
            urlRequest.setValue(header.value, header.key)
        }
        return urlRequest
    }

    private fun buildPostRequest(request: Request): NSMutableURLRequest {
        val urlRequest = NSMutableURLRequest()
        urlRequest.setHTTPMethod(HttpMethod.POST.name)
        urlRequest.buildURL(request)
        urlRequest.buildBody(request)
        urlRequest.setValue(Network.Headers.VALUE_CONTENT_TYPE, Network.Headers.KEY_CONTENT_TYPE)
        request.headers?.forEach { header ->
            urlRequest.setValue(header.value, header.key)
        }
        return urlRequest
    }

    private fun NSMutableURLRequest.buildBody(request: Request) {
        val body = request.body?.entries?.map { "${it.key}=${it.value}" }
            ?.reduceIndexed { index, acc, entry ->
                if (index == 0)
                    acc + entry
                else
                    "$acc&$entry"
            }
        val requestData =
            body?.let { NSString.create(string = body).dataUsingEncoding(NSUTF8StringEncoding) }
        this.setHTTPBody(requestData)
    }

    private fun NSMutableURLRequest.buildURL(request: Request) {
        var urlString = request.path
        request.query?.entries?.forEachIndexed { index, entry ->
            urlString += if (index == 0)
                "?${entry.key}=${entry.value}"
            else
                "&${entry.key}=${entry.value}"
        }
        this.setURL(NSURL.URLWithString(URLString = urlString))
    }
}
This is the actual implementation of iOS.
a
Looks good
Try extracting the method
a
same issue when extracting method
Copy code
private class HomeBootstrapper(
        private val userClient: UserClient,
        private val userCache: UserCache
) : ReaktiveBootstrapper<Action>() {
        override fun invoke() = fetchAccessToken(userClient, userCache)
}
a
I see it has inject. If it is injected lazy, that's might be a problem
The stack trace says, that a frozen object is being mutated
So you should identify which object exactly. Then we can either prevent it from being frozen, it prevent it from being mutated.
a
Copy code
Uncaught exception: kotlin.native.concurrent.FreezingException: freezing of [Holder(value=com.badoo.reaktive.looperthread.LooperThread.Message@3144408, endTimeMillis=71273908)] has failed, first blocker is com.printful.keychain.supporting.client.UserClient@3f96738kotlin.native.concurrent.FreezingException: freezing of [Holder(value=com.badoo.reaktive.looperthread.LooperThread.Message@3144408, endTimeMillis=71273908)] has failed, first blocker is com.printful.keychain.supporting.client.UserClient@3f96738
Now I only have to find which object in UserClient gets frozen.
a
I think now you added ensureNeverFrozen. This will show
who
is freezing the object, but we need
which
.
Better to inspect the original stack trace
Is suspect the inject delegate.
a
Yeah, you are absolutely correct. Changed lazy injections to eager ones and everything works as expected. Thanks, @Arkadii Ivanov! 👏🏻
a
Glad that helped!
🙇🏻 1