The second post in series ‘building a mobile app in Rust and React Native’.

Link to the first part of the series.

In the first part of the series we set up the project and cross-compiled rust for all platforms. Now it’s the time to display our hello world!

Let’s start with rust backend!

Rust

At first open your Cargo.toml and change the crate-type and import jni and libc .

link

Now a bit of explanation.

crate-type — We will build our crate as a static library and dynamic library. Static library will be used later on by iOS whereas on Android we will use dynamic library. You can read more about that here.

— We will build our crate as a static library and dynamic library. Static library will be used later on by iOS whereas on Android we will use dynamic library. You can read more about that here. libc — iOS will use this library for C primitives.

— iOS will use this library for C primitives. jni — Provides JNI bindings for rust. Default features are off, cause backtrace feature of this library does not compile on Android. It’s also marked as optional, cause we won’t use it on iOS.

— Provides JNI bindings for rust. Default features are off, cause backtrace feature of this library does not compile on Android. It’s also marked as optional, cause we won’t use it on iOS. features.default — By default we want to build everything (including JNI).

Our Cargo.toml is ready, now let’s write some code! Unfortunately, we will have create rust bindings for iOS and Android separately. Here is why

Android backend

Because we use JNI bindings for rust, our functions need to have signatures recognisable by JNI. Here how rust bindings for Java look like:

link

Java_com_mobile_1app_MobileAppBridge_helloWorld — this is signature expected by JNI.

— this is signature expected by JNI. JNIEnv — is context in which this function has been called. It can be used to call functions / create objects / throw exceptions / create classes and basically everything else. We use it only to get value of the reference we got.

— is context in which this function has been called. It can be used to call functions / create objects / throw exceptions / create classes and basically everything else. We use it only to get value of the reference we got. JClass — represents object of a class for which our function is implemented.

And that’s our backend for android application! Compile it, using make android and now, let’s connect it with ReactNative!

Android

At first we need to set up anroid.ndk and sdk.dir variables. I actually forgot to describe it in the previous part of the tutorial.

echo "ndk.dir=$NDK_HOME" > android/local.properties

echo "sdk.dir=$ANDROID_HOME" >> android/local.properties

link

Now let’s use AndroidStudio to open our project. We will create two new classes MobileAppBridge and MobileAppPackage and modify MainApplication

link

System.loadLibrary — loads our rust dynamic library.

— loads our rust dynamic library. private static native String helloWorld — is a declaration of our rust function.

— is a declaration of our rust function. @ReactMethod — is our javascript binding method

link

createNativeModules — loads our MobileAppBridge

link

getPackages — we need to add our package (which loads our rust bridge) here.

Now it’s finally the time to display our helloWorld on screen!

link

Voilà! Our app just displayed: rust says: Hello Android!

iOS

Now it’s the time to display Hello iOS! . It will require us to write slightly more code.

Let’s start with implementing String type which will pass over the FFI*.

link

link

So, what’s exactly happening here? Let’s start with hello_world function. It takes as input StringPtr (which can be either a str allocated by rust, or with other language’s allocator). We use this string to build response and then we return a pointer to it. Box::Into_raw(Box::new(response)) is basically a way to tell rust compiler to forget about deallocation of this variable.

*_destroy functions are used only to ‘remind’ compiler about allocation of those variables. At the end of the scope, the compiler deallocates them.

Now let’s link our rust code with javascript though objc/swift. First, compile rust code using make ios . Then open Xcode and add libresolv.tbd and libmobile_app.a

libresolv — libc links to it since may. link

— libc links to it since may. link libmobile_app.a — that’s our rust static library is in rust/mobile_app directory.

We also need to add libmobile_app.a to Xcode project library search patch

Congratz! Our rust library is properly link to Xcode project! Now let’s write some c headers for our rust library.

link

We’ve seen all of those functions already in rust. There is nothing new to describe. Next let’s create an extension for Swift’s String to ease ffi usage. Create new file called String.swift . When asked if Xcode should create Bridging Header, press yes. We will use it later.

link

Bridging Header is used to expose header files to swift. Let’s fill it with:

link

Uff, we are almost there! Now let’s create last two files MobileAppBridge.m and MobileAppBridge.swift .

link

link

RCT_EXTERN_METHOD — is a macro used to expose our function to ReactNative

— is a macro used to expose our function to ReactNative @objc func sayHelloWorld(_ — is a implementation of function declared in Objective-c. Please note additional _ . It’s important. I’ve lost about 30 figuring out why the code without it, does not compile.

That’s it! Our rust backend is now done. The javascript part is the same as for Android, so I will skip yet another screenshot. We can now display Hello iOS! .

If you’ve reached this point, thank you for reading. We’ve connected all rust with javascript and from now on, development should be easy. I will describe some of the issues I had in next article. And I had several. They were caused mostly by still buggy react-native and have nothing todo with rust. I’ve also created a boilerplate project if you would like to play with rust-react-native by yourself.

If you liked the article, please retweet.

Links

rust-react-native-boilerplate — boilerplate project to get started.

parity-signer — production-ready app written in react-native and rust

follow me on twitter

follow me on github

*As Redrield noticed StringPtr is actually redundant. link