Hello, I'm starting to explore KMM and I have a ba...
# multiplatform
d
Hello, I'm starting to explore KMM and I have a basic question that I didn't notice in official doc: how to actually consume coroutine / suspend function in ios module? And especially in a ViewController? Thanks.
t
Hello there. In my company we consume all return values from KMM code as flows. So we basically dont call suspend function from iOS code at all.
This is how we wrap the Flow, to be easily consumed in iOS code:
Copy code
class CommonFlow<T>(private val origin: Flow<T>) : Flow<T> by origin {

    fun watch(block: (T) -> Unit): Closeable {
        val job = Job()

        onEach {
            block(it)
        }.launchIn(CoroutineScope(job + Dispatchers.Main))

        return object : Closeable {
            override fun close() {
                job.cancel()
            }
        }
    }
}
Maybe somebody will give you better answer ๐Ÿ™‚
j
๐Ÿ‘€ 1
๐Ÿ‘ 1
d
Thank you both. ๐Ÿ™‚ Very nice answers
j
fwiw wrote short post recently about using that library.....in this case using new Swift 5.5 concurrency approach (but library also supports Combine and RxSwift as well) https://johnoreilly.dev/posts/kmp-native-coroutines/
๐Ÿ‘ 1
o
I don't know where I saw it but we use almost the same as @Tom Wayne
Copy code
expect class CommonFlow<T>(origin: Flow<T>): Flow<T>

fun <T> Flow<T>.asCommonFlow(): CommonFlow<T> = CommonFlow(this)

//Android
actual class CommonFlow<T> actual constructor(origin: Flow<T>): Flow<T> by origin

//iOS
actual class CommonFlow<T> actual constructor(origin: Flow<T>): Flow<T> by origin {
    val scope = MainScope(Dispatchers.Main)

    fun observe(block: (T) -> Unit): Closeable {
        onEach { block(it) }.launchIn(scope)

        return object : Closeable {
            override fun close() {
                scope.cancel()
            }
        }
    }
}
I will also add how we close the returned closeable from Swift side, so someone can tell me if I did something wrong ๐Ÿ˜… . I'm not sure if I'm creating a retain cycle here :
Copy code
public typealias Disposal = [Disposable]

extension Ktor_ioCloseable {
    func add(to disposal: inout Disposal) {
        let disposable = Disposable(self)
        
        disposal.append(disposable)
    }
}

public final class Disposable  {
    private let closable: Ktor_ioCloseable

    init(_ closable: Ktor_ioCloseable) {
        self.closable = closable
    }

    deinit {
        print(":::deinit is calling")
        closable.close()
    }

    public func add(to disposal: inout Disposal) {
        disposal.append(self)
    }
} 

//on the call site
private var disposal = Disposal()

myFlow.observe { state in
            ///...
}.add(to: &disposal)
๐Ÿ‘ 2
b
@Osman Saral I have the same thing implemented and i've been looking to see if retain cycles get created but none so far
o
Great thanks @Bolu Okunaiya