This article aims to discuss code sharing between Android and iOS using Kotlin Multiplatform. You may or may not have the knowledge of Android and ...
For further actions, you may consider blocking this person and/or reporting abuse
Hi Kurt! Great article but I still have a question:
"Suspending functions aren't compiled to ObjC so we can't use those on iOS" and yet you defined the following function
suspend fun getItems(): List = client.get("url.only.fortest/items")
which is a suspending function. Are they compiled to Objective-C or not?
You can still use Coroutines but only in Kotlin. You won't be able to call them in Objective-C.
So what's the point of the getItems() function if it cannot be called on iOS? Or is it meant to be used only inside the multi-platform code itself?
It's being called on Kotlin's side through the
ViewModel. TheViewModelcalls it and exposes it through aCFlowwhich is just a wrapper of aFlowwith an extrawatchfunction that we can use in Objective-C.Edit: More explanation (I was on a phone earlier and it was hard)
To consume a
Flow, you can usecollectwhich has this signaturesuspend fun Flow<*>.collect(): Unit. Since this is a suspending function, you won't be able to call this in Objective-C.suspendfunctions' interoperability are still unsupported so we expose a normalwatchfunction that takes a lambda and handles launching the coroutine.I take no credit on the
watchfunction. The guys from Jetbrains did a good job on it. github.com/JetBrains/kotlinconf-ap...Hi Kurt!
First of all, in my opinion this is a great article. :D
I just don't agree with this:
"For Android developers:
ConflatedBroadcastChannel = MutableLiveData
Flow = LiveData"
More like:
ConflatedBroadcastChannel = (MutableLiveData - LifeCycleAware)
And:
(Flow + Stores Latest Value) = LiveData
Also, isn't it possible to keep using LiveData (with Flow.asLiveData()) for Android and only use CFlow when working with iOS. I don't really get the goal of wrapping it to CFlow for both platform or am I missing something?
By converting Flow to LiveData over wrapping it with CFlow, you no longer have to worry about the "Closables" of CFlow (atleast in android). Whatcha think?
Its actually awesome to see a fellow filipino write articles about Kotlin Multiplatform. Awesome dude awesome! :D
Hello! Glad to see another Filipino interested in this!
You're right about that!
ConflatedBroadcastChannelis aMutableLiveDataminus the lifecycle awareness.Flowhowever, is just theConflatedBroadcastChannelwhich we just change intoFlowto prevent the consumers from overwriting the value.Yes, you can use
Flow.asLiveData()sinceCFlowis still aFlow. You can use whatever you like but for me, I usewatch()so that iOS devs aren't that far from it just in case they want to peek. I had to write some lifecycle management code, though, for theCloseable. It's much like theDisposablein RxJava2 so I created a baseFragmentwhich would handle the lifecycle management.I think that would make sense since
CFlowis just for iOS.Maybe an expect/actual implementation would do?
Thanks for your response!
The disadvantage in usage the snippet above though would be for testing.
Since
CommonFlowisn't aFlowincommonMain, unit tests incommonTestturn into red. For that reason, I still used theCFlowbut just usedasLiveData()in AndroidAha! I see, it is because you want to make the Android ViewModel and iOS "ViewModel" seem to be just the same.
Hmmm... In my opinion, and this is purely my opinion, I would keep Android ViewModel and whatever iOS "ViewModel" seperate from each other. They are platform specific concepts and I don't think its good idea to mix them as one. I would rather therefore move all logic outside these "ViewModels" which would make these "ViewModels" just "wrapper" or "wiring class".
This would make me safe from any changes these platform specific constructs. :) But then again just my pure opinion. I guess it is at this point that developers tend to argue. Until what layer should be shared?
Not really. The
ViewModelis already different based on the platform. You can checkBaseViewModelwherein on Android, it uses Architecture Components, and on iOS, it's just a custom implementation.I beg to differ, the ViewModel can be shared. Ideally, it shouldn't have a dependency on the platform's framework. You have the view (aka
Fragment/Activity/ViewController) for that. For example:There's no platform-specific implementation. It's just pure logic.
The
watchfunction on theCFlowclass is just sort of an extension for iOS which you can choose to use or not in Android. iOS cannot getsuspendfunctions which is why we have to resort to some workaround for now.You can choose to use the
expect/actualimplementation forCommonFlowand use the normalFlowin Android and useCFlowfor iOS however this makes you unable to test it incommonTestsince this sourceSet will usecommonMainand in the declaration, it's not aFlowand you can't let andexpectdeclaration inherit from something so you have to create both of the tests inandroidTestandiosTestI think exposing a function doesn't hurt. It's not like we changed the whole implementation. We just added something for it to be used on the other side.
But that's my opinion and what's worked for me so you can still go ahead and try your implementation out and see if it works for you. :)
TL;DR Add a
watchfunction so we can useFlowon iOS vs. Write them separately on each platform. I go for usingwatchNicee! :D Keep writing and Happy Coding! :D
Glad to hear about Kotlin Multiplatform. I was getting a little worried about "going all in" career-wise by focusing on cross platform development with React Native. It looks like cross platform development is here to stay!
Glad to hear you are glad to hear about KMP 😄
I love your enthusiasm and want to be sure you understand the cross platform development with React Native is very different than cross platform development with Kotlin Multiplatform.
Kotlin Multiplatform Can’t Do It All. Which is Exactly Why You Should Try It!
Justin Mancinelli ・ Aug 20 '19 ・ 4 min read
Cross-Platform? We Don’t Say That Around Here Anymore
Touchlab ・ ・ 3 min read
Medium
Yes, cross platform is here to stay. Code sharing has been with us since the first cross-compilers and transpilers that allowed the same code to run on different architectures. Since then, there have been many solutions, some that people still use, some that never gained traction. It's a super interesting space.
Hi Kurt!
Thank for this article, I have a question. In the android studio, how can we turn on autocomplete code when writing code inside package iosMain?
Hi! Autocomplete works for me out of the box but if you're using libraries from cocoapods then you have to build the project first on Xcode.
Currently, I just using the sample project without cocoapods but android studio no turn on code suggestion and autocomplete. Example "dispatch_get_main_queue" and "dispatch_async". is it need another plugin?
How are you configuring your native targets?
this is my config
I following from github link github.com/adrianbukros/github-mul...
Sorry for the delayed response. With your configuration, you're having two native source sets,
ios_x86_64Mainand ios_arm64Main. Autocomplete should work for both of those. However, I looked at the repo you linked and it's creating a common source set for both of those so you can code it iniosMain`. I'm afraid there's still an issue with this.`kotlin`
gradle.properties.kotlin.mpp.enableGranularSourceSetsMetadata=true
did it, Thank you so much!
Hi Kurt, it looks to me that everything is on the main thread or am I missing something?
You got that right. This is because of K/N. Once Multithreaded Coroutines come out on Native, we can implement switching threads. :)
Hey Kurt, thanks for the really well written article. I'm wondering,
how or why the ui is not causing visible "jank" or not getting android.os.NetworkOnMainThreadException for network calls if everything is on the main thread like Petar said?
Thanks! You can take a look a this.
But we cannot use with context dispatcher.io with kn as I read kn doesn't have multi threaded coroutines. My question is how the main thread doesn't get block? For example, I need to retrieve and save data from sqlDelight, Android and iOS. Database actions can be long running operations, if kotlin native just accepts kotlin coroutines single thread, so I cannot use dispatcher.io how can I do that?
So they now work in progress to support multi threading. Pull request