Combining the best of two cross-platform frameworks to build the ultimate multiplatform development experience.
React Native Kotlin Multiplatform Toolkit
Transcription
Okay, thank you for the nice introduction. As I said earlier, I'm Eric and with me is Leon and we want to talk about the combination of react Native and Kotlin Multiplatform to create the ultimate cross-platform setup for developing iOS and Android apps. So first let me start to introduce Voice a little bit. Voice is a company that's built speech recognition for healthcare. We have an application where healthcare workers can speak freely into our application, read and transcribe the text and create structured medical records from the transcription. Then we are able to use an integration into the existing infrastructure in the facility to sync these records into the existing electronic health record systems. This is what we do and you can see it in the GIF. And this way we already created over 1 million medical records and our app is powered by state-of-the-art custom speech recognition that we fine-tuned for the healthcare use case and all our models run on device offline so a facility does not have to have full Wi-Fi coverage to use our application. We won't be here if this had nothing to do with react. Our application is built with react Native so all the UI that you see here is built in react Native. We still use many native functionality to do some heavy lifting and we will talk about this later. So who here in the audience has wrote an app in react Native? Oh nice, cool. So and who wrote a native module for react Native before? Yeah, still a few, nice. And who has a big chunk of their code base in native? And who thinks about rewriting parts of their JS implementation in native? Okay, interesting. So we have kind of three personas in this room. I think the first persona is the most represented one is people who have all or most of their code written in JS. And they have only very little native code, maybe only the boilerplate that is needed for react Native to work and their kind of use of the shelf open source libraries that already did the work and wrapped native functionality into native modules so they are available in javascript. The other one, probably not so represented here, is people who have like their full stack app in Android and iOS completely custom written and they have only very little JS code or even just thinking about introducing react Native to streamline some of their application to be cross platform. And the third person group maybe just mixes JS and native and uses whatever they need at the moment and what fits their needs the best. So why are native modules so awesome and why we need them in react Native is the first one is the most obvious reason we want to access native functionality. We can't just do everything in javascript. We kind of have to have access to more platform specific stuff. And this is also what most react Native libraries do. They kind of just have an Android implementation for some functionality, iOS implementation. And then they kind of create a native module so they create a common interface for this native functionality and provide you with a JS interface to kind of just call it and you can consume this library from npm. But they're all, there are also use cases like you have existing code like in Android and iOS and for example you have some state that is stored natively but you want to have UI that can show the state or update the state so you kind of have to create native modules to do the communication between the stuff in native and the stuff in JS. And also maybe the people who want to migrate parts of their stuff from JS to native, they do it for performance reasons or to leverage concurrency that you have a native to just separate work from the JS thread. So but why do so few people actually write native modules? Probably the first one is that react Native has a really good ecosystem and there are well maintained libraries for most of the native functionalities that you would need. So there is not so much of a case for writing native modules all the time because the library support is very good. But if you do need to write native modules it's pretty difficult because you need to be familiar with the Android tech stack, with the iOS tech stack and tool chain you need to for example you want to kind of build a Bluetooth manager, you have to implement it in iOS, in Swift or Objective-C and you have to implement in Android, Kotlin or Java and then you also have to create these adapters, native modules that kind of map data structures and arguments from native to JS and also you need to kind of expose via event handling with a native module or kind of migrate exceptions or convert exceptions to promise rejections in your native module. And you have so much boilerplate to just expose the functionality that you've just written in native. And also there's nothing inherent about native modules that keeps the interfaces in sync. So you are fully responsible for keeping the interfaces Android, iOS and JS in sync so everything works together. And if you have an existing code basis in iOS and Android it's pretty hard to use react Native in the first place because they maybe are maintained by different teams or in different repositories and it's hard for those teams to kind of make an agreement on an interface and create a react Native module that can then be used in a like share project which has like react Native components. So these issues lie at our heart because apart from the normal, apart from the libraries that exist that have native modules in them, we also have over 20 custom native modules that we've written to do communication between JS and native because we do our auto processing in native and we need to communicate data around, we do all our model inference obviously in native, managing model artifacts, downloading stuff and so on and doing background work in general. So this is why we need a solution for this. If you do so many native modules and expose so much native functionality, you need to have a better way than doing all this boilerplate and Leon will tell you more about the status quo and what we can do to improve. So this is a typical react Native project setup. You have actually three sub projects for Android, iOS and react and each of these projects use their own language and own ecosystem and their own tools. So if you want to expose native code, you would have to write it twice for each native platform and when you write react Native modules, the api to expose it is very low level and platform dependent. So imagine you have to write 20 react Native modules to expose your native logic. There must be a better way to leverage native concurrency and native performance improvements in your react Native app. So let's introduce Kotlin Multiplatform Mobile. With Kotlin Multiplatform, you are able to run your Kotlin code on iOS and Android. Unlike JS code with react Native, it is able to run on multiple platforms, but unlike react Native, Kotlin is compiled directly natively to the platform specifics. So it has no runtime in between. And therefore, because it's compiled to the platform, it also allows you to use platform specific APIs directly without needing extra libraries which create a wrap-up for the native platform specific APIs. To get a better understanding how Kotlin Multiplatform can be used together with react Native, Eric will show you a demo. Okay, so let's switch to the demo. Here we have a Kotlin file and this Kotlin file is within a Kotlin Multiplatform project. So here we are in the common main folder and in the Kotlin Multiplatform project, this is a special folder also called a source set. Everything that we write here is platform agnostic. So it's cross-platform code that is written in Kotlin and like Leon said, will be compiled natively to JVM on Android and to native for iOS. So what we have here is a name manager that has two methods, setName and getName and setName with row and exception if the name is not one of the valid ones, Eric or Leon. And internally it uses persistent config to set this in a persistent way in native. So let's look at persistent config. And you can see persistent config is just like kind of an interface, but there's the expect identifier there. And this means that we expect that there is an actual platform specific implementation for this class on iOS and on Android. And let's look into this. So I can go here and look into the Android one. And you can see now we are in the Android main source set. And this means we can access Android specific APIs. So I can just import from Android and if I want to have a key value store in Android, that's pretty simple. I just use share preferences and I can import from Android, get from the context to share preferences for our bundle and then implement our methods that are defined by the expect class. Similarly I can go to the iOS implementation and this is where it gets interesting. Because now we are in the iOS main source set and we can access platform specific APIs from iOS. And this is kind of remarkable because imports like import from foundation are usually imports you only have an objective C or Swift. But with Kotlin Multiplatform you can just import and it's user defaults and what it does it will have, it will ultimately generate an interop interface to this native api. So we can just code against this native api in Kotlin and Kotlin Multiplatform will do the rest and link and compile against this native platform. So key value store on iOS we just use NSUserDefaults and kind of implement our methods. So back to our common code and now we can kind of understand how this works. You have common code and whenever we need platform specific code we define an expect class that is actual implementations on both platforms. But in common code we can do like whatever we want in cross platform. And that's super cool. We can kind of mix cross platform and platform specific code. So now we have this cross platform name manager that's super cool that writes into native state. But how do we get this super cool cross platform class into react Native where we need it? So what we've built is you can just add an annotation. It's called react Native module and give a name. And this automatically exposes this common code class to react Native. And you can see we import from our tool kit which is named react Native Kudrit with a K for Kotlin. That's a joke. And now we expose the common code class. So but we're not done yet. We need to also expose the methods that we need. So let's expose method here. And now we exposed our complete common Kotlin class. And this is super cool. And now let's switch to JS. This is a basic app where I can set the name with an input field. I can in a button and I can refresh to get the name from native. So currently this doesn't work at all because it doesn't really do anything when I say get name or set name. But you can see that this works because what we're loading is to its thing. And now we want to implement our common class that we just exposed. And we can do this via the native module setup. So we just say name manager is from native modules. And say, okay, let's do the set name implementation. And we have a promise that we await set name from input. Okay. You remember when we input a name that's not ErgoLeon, it will throw an exception. So let me just say Adam. And you can see the exception that was thrown in Kotlin code is now mapped to promise rejection. So let's set a name that's valid. Nice. But we still don't have any get config. So we expose the get config. So we just set our name here to await name manager, get name. And now we can refresh and have Leon. So we use this, the methods that we just exposed also should work on iOS. Fine. And when I say something different, yeah. Okay. So, but now we always have to press refresh to get the name from native. And maybe we can do better. So let's go back to our Kotlin code. And what's hidden here underneath is name is flow. And this uses the persistent config class, which defines get config as flow. You don't have to know too much about Kotlin flows, but you kind of can subscribe to them. And then they notify you whenever there's a new value. So let's try to use this and make a new react native method, which is a suspend function called name, which has this interesting argument and say name is flow to react previous. So now we exported this interesting function to react native. And now let's see, we just come in out the local state that we have here for name and just say name, use flow, name manager dot name. Now let me enter another name. And it automatically switches to the config because we subscribed to the native flow. Yeah. And this is what the toolkit can do. It can kind of you can easily export common code. But since we are in common code, we can add utilities to make other things even easier. For example, like directly expose Kotlin flows because Kotlin flows are awesome. And why not have utilities to directly export them using our toolkit. So there's kind of a Kotlin dependency. That's the toolkit. And there's also a javascript dependency for the utilities that we just used. OK, so this was a demo. Switch back. And quick recap. We saw Kotlin multiplatform action. We have common code, platform specific code. We can mix them and we can use expect actual to create a kind of an interface to have and create an abstraction on platform specific implementations. And we can use react Native Toolkit to directly expose common code classes to react Native via a native module. And use kind of to react and use flow and utilities to make our lives easier to work with Kotlin code. So what problems are solved by combining Kotlin multiplatform with react Native? First and foremost, it's the streamline the native implementation via KMM. And also we reduce the boilerplate code using our toolkit because you don't have to yourself create a native modules, but they are just generated using the annotation. So using Kotlin multiplatform, it is guaranteed that the interfaces for the different platforms stay in sync. And what also be nice is that we can now use, we can now migrate native code to Kotlin multiplatform and then directly and easily expose it to react Native. And now let's have a look at how the project structure changed with this setup. Now we have a single code base, which is in Kotlin for our native code. And in this project, there are three source sets. One common code source set, which should contain the most of your business logic, which you are sharing between all platform. And there are only small portions of your code are actually platform specific for Android and iOS. And now we can also see that react Native modules only have to be defined once in a common code and not for both platforms. And we think that this is the ultimate best platform set up for mobile development, because you can write your UI with react, implement your business logic natively with Kotlin multiplatform. And between you have the react Native toolkit, which helps you move between them. And how you get started. We created a react Native toolkit, GitHub repository. Has a nice guide, which helps you set up and get started with react Native toolkit, but also helps you in using Kotlin multiplatform with react Native project. So thank you very much for listening. Please check out the repository that I just mentioned. It's fresh off the press, like released like two weeks ago. And leave us a star. And also, if you think it's interesting what we're doing, like bridging the gap, react, Kotlin and machine learning and doing something for healthcare workers, please consider reaching out to us. We are hiring for react and Kotlin engineers. You can check us out on voice.de. And we are happy to get your questions. Wow. A really nice presentation. I mean, I'm blown away. I didn't know about this package. So yeah, thank you very much for first creating it. It seems like it's really easy to just integrate everything, which is really nice. But I would like to invite you here. Just maybe we can go through some questions. We do have some questions. That's nice. But meanwhile, while I'm going through them, could you please add more about the new architecture and the old one? Have you ever thought about it? Or what do you think it's like? Is there a good fit for this library? Yeah, so we just had the talk about the new architecture for Nikola. And it's super amazing what the react Native team has done. And we're kind of going a little bit different approach. But we've talked to Nikola and to the react Native team to kind of adapt this library to also generate modules that are more fitted to the new architecture. Also Nikola said that all the modules that we generate now are still backwards compatible with all the still in the new architecture. But we can go further and also kind of generate code from Kotlin that's compatible with the new architecture. That's great. Yeah, thank you very much. There's another one. What do you think about the new native modules api from Expo? Let's leave this question for later. Can an independent native module be created to be imported in a bare react Native project? Or does this whole project needs to be created with this? I think he's referring to the library itself. I can answer this question. So we generate native react Native modules. We can import them in other projects. And we're actually doing this with our app. And you have to keep in mind that by generating multiple react Native modules, they cannot be auto linked. So you have to manually link them in your code. But that's also a nice feature to have, because then you can inject dependencies into react Native modules, which would not be possible if they are auto linked, because they are the constructor. You have to be calling yourself and can then give some extra arguments in there. Alrighty, and there is another one. Would it be possible to support react Native Web 2? Either with Kotlin to JS or WASM compilation? Difficult, because this is only for react Native. I don't think if react Native is also supported for the web. There's also react Native Web. But we haven't looked into it. But it's probably interesting because Kotlin, as you said, Kotlin Multiplatform can only compile to JS. So maybe it's interesting if something could be done there to kind of interop with more browser specific stuff and kind of write native modules for browser specific stuff. There was also another question in regards to this compilation to javascript for react Native UIs in general. So yeah, I think that question also got addressed. And how does this toolkit work with building native animations? I think this is like a vague question in one way or another, but maybe leveraging native modules that have animations pre-built, I think, something. I think what I would imagine that you have maybe some flow that does an animation, but you have to remember that we are still going over the bridge and you will still have a render update if you receive something from use flow. So doing like super heavy animations over this use flow is still not really, it doesn't really do it. You should still use kind of react Reanimator that does it a little bit differently. So you don't have this, this problems. So it's more basically was a use case fire and forget not to get back any info from the native side. No, no, it's for both sides, both sides, but you shouldn't rely on it to do like rapid fire information from native. It just is for business logic, normal state updates, all the stuff that you would normally write like with a Kotlin setup. I'm curious to see if there are any open source libraries built with react Native Toolkit out there already. Currently not, because it's like we said, it's fresh from the press. We released it two weeks ago. And have you ever, do you have any plans on open sourcing some of those 20 different native modules except the business logic, of course, and give it to public? The thing about these 20 native modules, they are very tightly coupled to our application business logic inside our application. So that's why we have built these custom modules because they are exposing, especially this logic we have in our code and cannot use some public off the shelf library because they off the shelf libraries don't integrate so well with our own business logic we have already in native Kotlin code. And this is also what Leon talked about, like dependency ejection is a super important topic. And most of the natives that we have, have like dependency ejections of dependencies that are super tailored to our application, like our model manager, our processing and inferences and stuff. Someone is saying greetings from the office. I totally agree. Yeah, it was like a mind blowing presentation. Yeah, I'll definitely give it a try. There is another question about what do you think about the new native modules from Expo? And what do you think it might be the difference? You mean Expo or you mean react Native? Expo native modules. We are not familiar with Expo at all. Okay. That kind of makes sense. Because yeah, it's good to live in your own world in a way or another to come up with different solutions and not be like influenced by others, which I think this is like what you have presented today is something new, something fresh from my opinion, because I mean, this react Native ecosystem for a while, I've never seen anything like this. And yeah, I'm really happy to be like here in the first row to see it actually in action. And yeah, I would like to say congrats once again. And yeah, it was an amazing talk. Thank you so much for giving this presentation and we're looking forward to see where this package is going to go through. Thanks. Thanks. All right. Thanks, everyone.