Skip to content

Instantly share code, notes, and snippets.

@danailalexiev
Created October 5, 2022 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danailalexiev/907e41751eec1c40b44b6da8fcbee990 to your computer and use it in GitHub Desktop.
Save danailalexiev/907e41751eec1c40b44b6da8fcbee990 to your computer and use it in GitHub Desktop.
Kotlin Multiplatform iOS Coroutine Interop
package com.infinitelambda.chuck.util
import kotlinx.coroutines.*
import platform.Foundation.NSError
import platform.Foundation.NSLocalizedDescriptionKey
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.native.concurrent.SharedImmutable
import kotlin.native.concurrent.ThreadLocal
import kotlin.native.concurrent.freeze
@ThreadLocal
internal object PlatformDispatcher {
private var injectedMain: CoroutineDispatcher? = null
private var injectedDefault: CoroutineDispatcher? = null
val Main = injectedMain ?: Dispatchers.Main
val Default = injectedDefault ?: Dispatchers.Default
fun injectMainDispatcher(dispatcher: CoroutineDispatcher) {
injectedMain = dispatcher
}
fun injectDefaultDispatcher(dispatcher: CoroutineDispatcher) {
injectedDefault = dispatcher
}
fun resetDefault() {
injectedMain = null
injectedDefault = null
}
}
@SharedImmutable
internal val iosScope = CoroutineScope(PlatformDispatcher.Main + SupervisorJob())
typealias CompletionHandler<T> = (T?, NSError?) -> Unit
interface NativeCancellable {
fun cancel()
}
internal fun <T> CoroutineScope.withNativeCompletionHandler(
completionHandler: CompletionHandler<T>,
context: CoroutineContext = EmptyCoroutineContext,
block: suspend () -> Result<T>
): NativeCancellable {
val job = launch(context) {
withContext(PlatformDispatcher.Default) { block() }
.fold(
{ completionHandler(it.freeze(), null) },
{ completionHandler(null, it.asNSError().freeze()) }
)
}
job.invokeOnCompletion {
if ((it == null) || (it is CancellationException)) {
return@invokeOnCompletion
}
completionHandler(null, it.asNSError().freeze())
}
return object : NativeCancellable {
override fun cancel() {
job.cancel()
}
}.freeze()
}
internal fun Throwable.asNSError(): NSError =
NSError.errorWithDomain(
domain = "KotlinException",
code = 0,
userInfo = mapOf<Any?, Any?>(
"KotlinException" to this.freeze(),
Pair(NSLocalizedDescriptionKey, message ?: "An unexpected error occurred")
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment