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
. TheViewModel
calls it and exposes it through aCFlow
which is just a wrapper of aFlow
with an extrawatch
function 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 usecollect
which has this signaturesuspend fun Flow<*>.collect(): Unit
. Since this is a suspending function, you won't be able to call this in Objective-C.suspend
functions' interoperability are still unsupported so we expose a normalwatch
function that takes a lambda and handles launching the coroutine.I take no credit on the
watch
function. 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!
ConflatedBroadcastChannel
is aMutableLiveData
minus the lifecycle awareness.Flow
however, is just theConflatedBroadcastChannel
which we just change intoFlow
to prevent the consumers from overwriting the value.Yes, you can use
Flow.asLiveData()
sinceCFlow
is 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 theDisposable
in RxJava2 so I created a baseFragment
which would handle the lifecycle management.I think that would make sense since
CFlow
is 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
CommonFlow
isn't aFlow
incommonMain
, unit tests incommonTest
turn into red. For that reason, I still used theCFlow
but 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
ViewModel
is already different based on the platform. You can checkBaseViewModel
wherein 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
watch
function on theCFlow
class is just sort of an extension for iOS which you can choose to use or not in Android. iOS cannot getsuspend
functions which is why we have to resort to some workaround for now.You can choose to use the
expect/actual
implementation forCommonFlow
and use the normalFlow
in Android and useCFlow
for iOS however this makes you unable to test it incommonTest
since this sourceSet will usecommonMain
and in the declaration, it's not aFlow
and you can't let andexpect
declaration inherit from something so you have to create both of the tests inandroidTest
andiosTest
I 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
watch
function so we can useFlow
on iOS vs. Write them separately on each platform. I go for usingwatch
Nicee! :D Keep writing and Happy Coding! :D
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_64Main
and 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 in
iosMain`. I'm afraid there's still an issue with this.`kotlin
`
gradle.properties
.kotlin.mpp.enableGranularSourceSetsMetadata=true
did it, Thank you so much!
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, 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