Rust-Rust android ios: rust_android_ios — An example of using a shared Rust lib for Android and iOS using rust-swig and cbindgen respectively.

Android / iOS app with shared Rust logic

logos

This is an example that shows how to communicate with a shared Rust library from an Android and iOS app. The idea is to be able to share domain logic & most services (networking, database, bluetooth, etc.) using Rust and use the native SDKs for presentation and platform specific services.

Why?

This approach gives us the best of all worlds! We prevent code duplication by using a shared library. Rust, as a highly performant and safe language is a great fit for mobile. We keep a fully native UI experience and uncomplicated access to the latest APIs of the platforms.

Supported

Functions

Calls Rust functions from Android / iOS, shows returned vale in UI.

Callback

Passes callback to Rust, update UI from callback with result.

Reactive

Subscribes in Android / iOS to events triggered by Rust and updates UI with them.

Unidirectional flow

UI sends event to Rust. Rust emits to channel that is observed in Android / iOS. This allows to e.g. implement MVVM architecture writing the view models in Rust (with some glue to convert the observer-callbacks in observables/channels/SwiftUI/Jetpack compose/etc).

JSON

Pass serialized Objects between Android / iOS and Rust using JSON

Android / iOS instructions

Ensure rustup is installed. This includes Cargo, so you should be able to compile & build Rust projects + install the required targets to build for Android and iOS.

List available targets:

rustup target list

Show currently installed targets:

rustup toolchain list

The Rust sources are here

android Android instructions

These steps show how to build and run an Android app in debug mode for a 64 bits emulator.

See rust_swig documentation

NDK

Ensure the NDK is installed.

Environment variables

ANDROID_TOOLCHAINS=<Directory where targets should be installed> 
ANDROID_NDK=<NDK's root directory>

Add targets

rustup target add x86_64-linux-android

Add path to linker

[target.x86_64-linux-android]
linker = "<Directory where targets were installed (provided in environment variable)>/android-29-x86_64-4.9/bin/clang"

Build

./gradlew assembleDebug

Install

./gradlew installDebug

Run

Ensure adb is installed.

then:

adb shell am start -n com.schuetz.rust_android_ios/com.schuetz.rust_android_ios.MainActivity

OR

Start the app in the emulator / device!

OR

Run the project in Android Studio. This will build, install and run.

Relevant configuration files

If you want to add targets or tweak the configuration, you have to edit one or more of these files:

  • App's Gradle config: This contains the apk settings (like application id, sdk version) and build steps. It builds for the provided architectures using cargo and puts the generated shared libraries (.so files) in the expected directories. If you want to build for a new target, add it here. The target's key is the folder name where the shared library will be put, and the value is the toolchain's name used by rustup.

  • Cargo config: Contains linker paths for targets.

  • build.rs: This is a script invoked by Cargo before everything else. For Android, it's used to tell rust_swig to generate the glue files for Java interop. If you change the app's package structure / names, you have to update this file accordingly. It also sets the import to use for the NonNull annotation ( use_null_annotation_from_package). If you're using a recent Android SDK version, you don't need to change it.

Updating Rust

You edited something in Rust! Now you have to:

  • Update java_glue.rs.in accordingly. This is a file rust_swig uses to generate the JNI glue. Consult rust_swig docs for syntax.

  • Build, install, run, as described above.

Updating Kotlin/Java

The code of the Android app can be found here. This is a regular Android app which you can work with normally. Just don't modify the generated JNI files and remember to update build.rs as described in Relevant configuration files, if you change the package structure.

iOS iOS instructions

App code

The iOS project is here.

Add targets

rustup target add x86_64-apple-ios

Build & run

From the project's root directory:

cargo build --target=x86_64-apple-ios

This will generate the required library files: <project root directory>/target/mobileapp-ios.h with the C headers and the (static) library for the target, <project root directory>/target/<target name>/libmobcore.a. At the moment you have to copy manually libmobcore.a into the iOS app's directory each time you update it.

With the header and library in place, you can run the iOS app.

Updating Rust

You edited something in Rust! Now you have to:

  • Update the C glue Rust implementation file. Orient with the existing code. Differently to Android the glue has to be written manually, because there's no library like rust_swig.

  • Build as described in "Build & run". Cargo will invoke build.rs, which uses cbindgen to generate the iOS library files.

  • Copy manually the generated <project root directory>/target/<target name>/libmobcore.a to the iOS app project's root folder.

  • Run!

Updating Swift/ObjC

The code of the iOS app can be found here. This is a regular iOS app which you can work with normally.

Wiki

Contribute

  1. Fork
  2. Commit changes to a branch in your fork
  3. Push your code and make a pull request

Credits

Based on parts of https://github.com/Dushistov/rust_swig/tree/master/android-example and https://github.com/terhechte/rust-ios-android-example

TODO

  • Pass / return struct pointers, casting / mapping to iOS structs and Kotlin classes (partly done already for iOS).
  • Avoid using global variables in iOS app.
  • Automate copying of libmobcore.a or reference properly & multiple targets.

Comments

  • iOS Github action
    iOS Github action

    Jul 6, 2020

    Configure iOS Github action (Unit Tests).

    good first issue 
    Reply
  • Flutter
    Flutter

    Jul 2, 2021

    I've been designing a Rust library for usage with Flutter. I see that you're doing Kotlin/Java <-> Rust here, but you mention Flutter.

    Is there a plan to make a template for Flutter? Because there are 2 methods to consider: FFI vs Plugin call. Since plugin calls are very slow, FFI should be prefered, but what to do when Rust needs JNI objects? You can't pass them through Flutter's FFI

    Reply
  • Requires Android NDK 22
    Requires Android NDK 22

    Oct 26, 2021

    Due to https://github.com/rust-lang/rust/pull/85806, this is broken by the last Android NDK (23). Symptom is failed link, unable to find library -lgcc.

    Workaround is simple enough, downgrade to NDK22, e.g.

    export NDK_HOME=${ANDROID_SDK_ROOT}/ndk/22.1.7171670
    

    Might want to make a note in the README?

    Reply
  • Big Sur, XCode 12.5.1, link fails
    Big Sur, XCode 12.5.1, link fails

    Oct 27, 2021

    ld: library not found for -lSystem
    

    Workaround is to add this block to ios_app/build-rust-xcode.sh

    # From https://github.com/TimNN/cargo-lipo/issues/41#issuecomment-774793892
    if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then
      # Assume we're in Xcode, which means we're probably cross-compiling.
      # In this case, we need to add an extra library search path for build scripts and proc-macros,
      # which run on the host instead of the target.
      # (macOS Big Sur does not have linkable libraries in /usr/lib/.)
      export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}"
    fi
    
    Reply
  • Add CI for Rust and Android
    Add CI for Rust and Android

    Jul 6, 2020

                                                                                                                                                                                                           
    Reply
  • Add information for rust-bitcode
    Add information for rust-bitcode

    May 3, 2021

    The installation instructions were missing for the iOS part. Also some of the import parts in the xcode project were wrong.

    Closes #4

    Reply
  • Few questions regarding for Android
    Few questions regarding for Android

    Dec 22, 2020

    Hi I have some questions regarding for the development of Android using the crate:

    1. Am I able to make GUI applications?
    2. Is there too much biolerplate code in it or is it simple and clean?
    3. Is it easy to use or harder to use over something like Flutter or Android app Studio?
    Reply
  • iOS dependencies
    iOS dependencies

    Jan 9, 2021

    Hey!

    Awesome project! I'm trying to run the iOS app (haven't tried Android yet) and I'm having some issues. Is there anything not in the README that's needed?

    I'm running on stable.

    When I run the iOS app in XCode I get

    + RUSTFLAGS='-Z embed-bitcode'
    + cargo +ios-arm64 build --target aarch64-apple-ios --release --lib
    error: no such subcommand: `+ios-arm64`
    + cargo build --target=x86_64-apple-ios --release
       Compiling log v0.4.8
       Compiling libc v0.2.60
       Compiling core-foundation-sys v0.6.2
       Compiling cfg-if v0.1.9
    error[E0463]: can't find crate for `core`
      |
      = note: the `x86_64-apple-ios` target may not be installed
    
    error: aborting due to previous error
    
    For more information about this error, try `rustc --explain E0463`.
    error: could not compile `cfg-if`
    
    To learn more, run the command again with --verbose.
    warning: build failed, waiting for other jobs to finish...
    error: linking with `cc` failed: exit code: 1
    

    I have installed x86_64-apple-ios and aarch64-apple-ios.

    Reply