loading...
Cover image for Building WASM, Android and iOS app with single/common RUST core code

Building WASM, Android and iOS app with single/common RUST core code

h_ajsf profile image Hasan Yousef ・3 min read

I'll write below, the file required to write simple hello world code using RUST, that can be called by WASM/Android/iOS app, compiling and calling will follow in later article.. follow me to be notified..

I tested both WASMand Android and they are fine.

src folder tree:

Hasans-Air:src h_ajsf$ tree .
.
├── lib.rs
├── android
│   └── mod.rs
├── ios
│   └── mod.rs
└── wasm
    └── mod.rs

3 directories, 4 files

And the files now are:

Main:

// lib.rs
pub mod wasm;
pub mod ios;
pub mod android;

#[cfg(not(target_arch = "wasm32"))]
use std::os::raw::{c_char};

#[cfg(not(target_arch = "wasm32"))]
use std::ffi::{CStr};

pub fn rust_greetings(to: &str) -> String {
    format!("Hello {}", to)
}

#[cfg(not(target_arch = "wasm32"))]
fn char_str(pattern: *const c_char) -> &'static str {

    let c_str = unsafe { CStr::from_ptr(pattern) };
    let string_ptr = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };
    string_ptr
}

WASM

// src/wasm/mod.rs
#[cfg(target_arch = "wasm32")]
pub mod wasm {
    use crate::rust_greetings;
    use wasm_bindgen::prelude::*;

    #[wasm_bindgen]
    pub fn wasm_greetings(to: &str) -> String {
        rust_greetings(to)
    }
}

Android

// src/android/mod.rs
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
    use crate::rust_greetings;
    use crate::char_str;
    extern crate jni;

    use self::jni::JNIEnv;
    use self::jni::objects::{JClass, JString};
    use self::jni::sys::{jstring};

    #[no_mangle]
    pub unsafe extern fn Java_com_hasan_RustGreetings_greetings(env: JNIEnv, _: JClass, java_pattern: JString) -> jstring {

        let jvm_input = env.get_string(java_pattern)
                                        .expect("invalid pattern string").as_ptr();

        let input = rust_greetings(char_str(jvm_input));

        let output = env.new_string(input)
                                        .expect("Couldn't create java string!");
        output.into_inner()
    }
}

iOS

// src/ios/mod.rs
#[cfg(target_os="ios")]
#[no_mangle]
pub mod ios {
    use crate::char_str;

    use std::ffi::{CString, CStr};
    use std::os::raw::{c_char};
    use crate::rust_greetings;

    #[no_mangle]
    pub extern fn ios_greetings(to: *const c_char) -> *mut c_char {

        let input = rust_greetings(char_str(to));

        CString::new(input).unwrap().into_raw()
    }

    pub extern fn iso_greeting_free(s: *mut c_char) {
        unsafe {
            if s.is_null() { return }
            CString::from_raw(s)
        };
    }
}

TOML

# Cargo.toml
[package]
name = "greetings"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"

[dependencies]

[target.'cfg(target_arch="wasm32")'.dependencies]
wasm-bindgen = "0.2.29"

[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.5", default-features = false }

[lib]
name = "rust_greetings"
crate-type = ["cdylib", "dylib", "staticlib"] 
# Android: dylib
# iOS: cdylib [armv7s-apple-ios] and staticlib [Others]
# WASM: cdylib

Below is to manage the linker for compiling Android targets

Cargo config:

# .cargo/config
[target.aarch64-linux-android]
ar = "NDK/arm64/bin/aarch64-linux-android-ar"
linker = "NDK/arm64/bin/aarch64-linux-android-clang"

[target.armv7-linux-androideabi]
ar = "NDK/arm/bin/arm-linux-androideabi-ar"
linker = "NDK/arm/bin/arm-linux-androideabi-clang"

[target.i686-linux-android]
ar = "NDK/x86/bin/i686-linux-android-ar"
linker = "NDK/x86/bin/i686-linux-android-clang"

[target.x86_64-linux-android]
ar = "NDK/x86/bin/x86_64-linux-android-ar"
linker = "NDK/x86_64/bin/x86_64-linux-android-clang"

Below are for running the WASM, which is already explained (here) [https://dev.to/h_ajsf/rust--wasm-using-bindgen-49b4]

webpack.config.js

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
  mode: "development"
};

package.json

// package.json
{
  "name": "utils2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.27.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

index.js

import("./pkg/greetings").then(wasmModule => {
  let gr = wasmModule.wasm_greetings("Karam sweet")
  console.log(gr);
});

index.html

<!DOCTYPE html>
<html>
<head>
    <script src="./index.js"></script>
    <head>
<body></body>
<html>

Posted on by:

Discussion

pic
Editor guide