https://www.artificialworlds.net/blog/2022/07/06/building-cross-platform-rust-for-web-android-and-ios-a-minimal-example/ Skip to content Andy Balaam's Blog Four in the morning, still writing Free Software Menu and widgets * RSS Feed * List of all articles * About Andy Balaam * Andy's home page * @andybalaam@mastodon.social * Tech videos * Support andybalaam on Patreon * Play Rabbit Escape! * Good Robot Andys (movie podcast) * Justice Worriers (Christian podcast) * Play web games on Smolpxl * Santa Circles Search for: [ ] [Search] Support the Software Freedom Conservancy Become a Conservancy Sustainer! Recent Posts * Why I'm voting tactically against you - letter to my Conservative MP * Rust WASM hello world - no need for webpack! * Estimating software tasks and stories: avoid time-based estimates * Maths: The Fun Parts - Graphs video * Maths: The Fun Parts - Groups video Categories * ACCU * Android * Ant * Async programming * bash * BOOST * C * C++ * Cloud * Diffident * Docker * Dojo * Elm * FreeGuide * Games * git * GNOME * Godot * Gradle * Graft * Groovy * GSSMP * Haskell * IGCC * j2ee * Java * JavaScript * JSON * Keybase * Kotlin * Lean and Agile * Linux * Lisp * Loki * Lubuntu * MATE * Matrix * Maven * Meta * Metaprogramming * Minecraft * Misc * My First Raspberry Pi Game * NNDB * PeerTube * Pepper * Performance * PHP * Pinephone * Programming * Programming Languages * Python * Rabbit Escape * Raspberry Pi * React * Record TV * Regular Expressions * rest * ruby * Rust * Scheme * Scratch * Scrum * Smolpxl * SQL * STL * systemd * Tech * Template Metaprogramming * Test Driven * Ubuntu * Uncategorized * Videos * vim * Writing * Xfce Credits Moon picture Albuquerque Moon by Jason Bache, used under CC-BY-2.0 @andybalaam@mastodon.social [prompt-bla]Building cross-platform Rust for Web, Android and iOS - a minimal example Toot Share on Mastodon One of the advantages of writing code in Rust is that it can be re-used in other places. Both iOS and Android allow using native libraries within your apps, and Rust compiles to native. Web pages can now use WebAssembly (WASM), and Rust can compile to WASM. So, it should be easy, right? Well, in practice it seems a little tricky, so I created a small example project to explain it to myself, so maybe it's helpful to you too. The full code is at gitlab.com/andybalaam/example-rust-bindings, but here is the general idea: * crates/example-rust-bindings - the real Rust code * bindings/ffi - uniffi code to build shared objects for Android and iOS * bindings/wasm - wasm_bingen code to build WASM for Web * examples/example-android - an Android app that generates a Kotlin wrapper, and runs the code in the shared object * examples/example-ios - an iOS XCode project where we generate Swift bindings, so we can call the code in the shared object * examples/example-web - a web page that imports the WASM and runs it Steps for WASM Proof that I did this on Web - Firefox showing "This string is from Rust!" * Write normal Rust code e.g. crates/example-rust-bindings/src/ lib.rs (and Cargo.toml etc. to go with it, with nothing special in it) * Write wasm-bindgen wrappers for all interfaces you want to expose e.g. bindings/wasm/src/lib.rs (and Cargo.toml to go with it, depending on wasm-bindgen). This basically involves creating structs and functions annotated with #[wasm_bindgen] that call through to the real underlying code from the previous step. * Write a package.json with a build step that calls wasm-pack e.g. bindings/wasm/package.json * Build the bindings with npm (see bindings/wasm/README.md) * Copy the generated .js and .wasm files into a web project * Include the generated .js file into an HTML file using an import statement in a module e.g. examples/example-web/index.html Variation: if you modify the build script in package.json to call wasm-pack with --target node instead of --target web you can generate code suitable for using from a NodeJS module. Steps for Android Proof that I did this on Android: Android emulator showing a label "This string is from Rust!" * Write normal Rust code e.g. crates/example-rust-bindings/src/ lib.rs (and Cargo.toml etc. to go with it, with nothing special in it) * Write a UDL file to describe your interfaces e.g. bindings/ffi/ src/my_rust_code.udl * Implement those interfaces in some Rust code, wrapping returned values in Arc e.g. bindings/ffi/src/lib.rs. * Write a Cargo.toml that builds the bindings e.g. bindings/ffi/ Cargo.toml * Generate uniffi bindings scaffolding during the Cargo build by writing a build.rs file e.g. bindings/ffi/build.rs * Get the Android NDK and set up Cargo's config to use it to build - see bindings/ffi/README.md * Cross-compile for the Android platforms you need by adding the targets using rustup and then building using cargo build --target =blah - see bindings/ffi/README.md * Copy the built .so shared object files into your Android project under `jniLibs/INSERT_PLATFORM` - see bindings/ffi/README.md * In your Android project, add a android.applicationVariants.all section to your app/build.gradle that generates Kotlin wrappers around the shared objects e.g. examples/example-android/app/ build.gradle * Now you can write normal Kotlin that accesses your Rust code via a namespace like uniffi.my_rust_code e.g. MainActivity.kt Steps for iOS * Write normal Rust code e.g. crates/example-rust-bindings/src/ lib.rs (and Cargo.toml etc. to go with it, with nothing special in it) * Write a UDL file to describe your interfaces e.g. bindings/ffi/ src/my_rust_code.udl * Implement those interfaces in some Rust code, wrapping returned values in Arc e.g. bindings/ffi/src/lib.rs. * Write a Cargo.toml that builds the bindings e.g. bindings/ffi/ Cargo.toml * Generate uniffi bindings - see bindings/ffi/README.md * Cross-compile for the iOS platforms you need by adding the targets using rustup and then building using cargo build --target =blah - see bindings/ffi/README.md * Combine together the built libraries using lipo - see bindings/ ffi/README.md * Copy the combined .a library file into your XCode project - see bindings/ffi/README.md * Generate an XCode project with xcodebuild - see bindings/ffi/ README.md * Now you can write normal Swift that accesses your Rust code [TODO: details] Toot Share on Mastodon Posted on July 6, 2022July 8, 2022Author Andy BalaamCategories Android, Programming, Rust, Tech 2 thoughts on "Building cross-platform Rust for Web, Android and iOS - a minimal example" 1. [9710b6] Patrick K says: February 24, 2023 at 4:52 pm Hi Andy, Thanks for the great intro/reference. We're currently looking at building a cross platform rust app targeting Android and Web. Being new to Rust, we're in the process of getting our heads around the idea that we must choose an async runtime to underpin async language features like futures. We don't have a need for multi-threading, but DX-friendly concurrency abstractions like futures and event-based/reactive programming will help us a lot in our particular domain which is heavily async. Do you have any recommendation for how to solve this cross platform? We've seen that WASM bindgen can convert futures code to promises, but this ultimately ends up in the JS layer. I guess what we're looking for is the ability for our rust module to use things like futures and events internally, and expose related methods/events/callbacks externally to JavaScript or Java/Kotlin code. A concrete example would be the ability for the rust code to schedule an HTTP request and await a response, where the making of request is delegated to fetch or OkHttp respectively, and the response returned. Any help much appreciated! 2. [31a715] Andy Balaam says: February 27, 2023 at 9:11 am Hi Patrick, no good advice except it's definitely worth looking at IniFFY: https://mozilla.github.io/uniffi-rs/ . Good luck! Leave a Reply Your email address will not be published. Required fields are marked * [ ] [ ] [ ] [ ] [ ] [ ] [ ] Comment * [ ] Name * [ ] Email * [ ] Website [ ] [Replies to my comments] Notify me of followup comments via e-mail. You can also subscribe without commenting. [Post Comment] [ ] [ ] [ ] [ ] [ ] [ ] [ ] D[ ] This site uses Akismet to reduce spam. Learn how your comment data is processed. Post navigation Previous Previous post: Deporting desperate people from the UK Next Next post: Matrix is a Distributed Real-time Database Video Proudly powered by WordPress