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

In this series I will describe process of connecting mobile front-end written in React Native with business logic written in Rust.

As an introduction to this article, I suggest reading a great series from John Gallagher: Building an iOS App in Rust, Part 1: Getting Started with Rust

The app we are building is a mobile wallet for ethereum cryptocurrency. It is able create brainwallet accounts, scan qr-codes with unsigned transactions and sign them.

The main reason why I decided to use Rust in this project was that I already had all modules written in it and ready to use (or at least I thought so).

Required tools

node.js (tested on v7.4.0 )

(tested on ) npm (tested on 5.2.0 )

(tested on ) rustup (tested on rustup 1.0.0 (17b6d21 2016-12-15) )

(tested on ) rustc (tested on 1.19.0 (0ade33941 2017–07–17) )

(tested on ) cargo (tested on cargo 0.20.0 (a60d185c8 2017–07–13) )

(tested on ) android_ndk (tested on r13b )

(tested on ) Xcode (only, for iOS, tested on Version 8.1 (8B62) )

(only, for iOS, tested on ) $NDK_HOME envarionment variable set to ndk home directory (eg. /usr/local/opt/android-ndk )

envarionment variable set to ndk home directory (eg. ) $JAVA_HOME envarionment variable set to java home directory (eg. /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home )

Setting up cross compilation

Once all dependencies are installed, lets set them up. We will start with installing all necessary toolchains.

# ios

rustup target add i386-apple-ios

rustup target add x86_64-apple-ios

rustup target add armv7-apple-ios

rustup target add armv7s-apple-ios

rustup target add aarch64-apple-ios # android

rustup target add aarch64-linux-android

rustup target add armv7-linux-androideabi

rustup target add i686-linux-android

Now, let’s create a new project.

react-native init our_project

Next, create rust subdirectory, enter it and run

cargo new our_project

After those steps, the project directory should look like this:

__test__

android

app.json

index.android.js

index.ios.js

ios

node_modules

package.json

rust

/our_project

/Cargo.toml

/src

Next, let’s setup makefile the project and place it inside our_project dir.

ARCHS_IOS = i386-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios aarch64-apple-ios

ARCHS_ANDROID = aarch64-linux-android armv7-linux-androideabi i686-linux-android

LIB=libsigner.a all: ios android ios: $(LIB) android: $(ARCHS_ANDROID)

sh copy_android.sh .PHONY: $(ARCHS_IOS)

$(ARCHS_IOS): %:

cargo build --target $@ --release .PHONY: $(ARCHS_ANDROID)

$(ARCHS_ANDROID): %:

cargo build --target $@ --release $(LIB): $(ARCHS_IOS)

lipo -create -output $@ $(foreach arch,$(ARCHS_IOS),$(wildcard target/$(arch)/release/$(LIB)))

copy_android.sh is a shell script that copies our statically compiled libraries to jniLibs directory.

#! /bin/bash mkdir -p ../../android/app/src/main/jniLibs

mkdir -p ../../android/app/src/main/jniLibs/x86

mkdir -p ../../android/app/src/main/jniLibs/arm64-v8a

mkdir -p ../../android/app/src/main/jniLibs/armeabi-v7a cp ./target/i686-linux-android/release/libsigner.so ../../android/app/src/main/jniLibs/x86/libsigner.so

cp ./target/aarch64-linux-android/release/libsigner.so ../../android/app/src/main/jniLibs/arm64-v8a/libsigner.so

cp ./target/armv7-linux-androideabi/release/libsigner.so ../../android/app/src/main/jniLibs/armeabi-v7a/libsigner.so

At this point we can already compile static libraries for android and iOS.

*disclosure

If your rust code links C libraries, the compilation will fail. To make it work, we need to set up custom linker. It can be done inside rust/.cargo/config file. You can use create-ndk-standalone.sh to generate it.

[target.aarch64-linux-android]

ar = "/Users/marek/projects/ethcore/native-signer/NDK/arm64/bin/aarch64-linux-android-ar"

linker = "/Users/marek/projects/ethcore/native-signer/NDK/arm64/bin/aarch64-linux-android-gcc" [target.armv7-linux-androideabi]

ar = "/Users/marek/projects/ethcore/native-signer/NDK/arm/bin/arm-linux-androideabi-ar"

linker = "/Users/marek/projects/ethcore/native-signer/NDK/arm/bin/arm-linux-androideabi-gcc" [target.i686-linux-android]

ar = "/Users/marek/projects/ethcore/native-signer/NDK/x86/bin/i686-linux-android-ar"

linker = "/Users/marek/projects/ethcore/native-signer/NDK/x86/bin/i686-linux-android-gcc"

At this point our project directory should look like like this:

__test__

android

app.json

index.android.js

index.ios.js

ios

node_modules

package.json

rust

/.cargo

/config

/our_project

/Cargo.toml

/copy_android.sh

/Makefile

/src

In next blog post I will cover making calls between Rust and React Native.

Project’s github repo.

follow me on twitter

follow me on github