This is a part of the post Mobile: A different way using Rust?.
Capacitor is normally used in combination with JavaScript projects, so there is little help available if you want to use it in a Rust/WASM project. Worry not! That's exactly what we'll take a look at in this post.
Since Capacitor is a JS framework there will be a little extra work involved in interacting with any plugins, but it’s honestly not a lot we need to do. Let’s take a quick tour through what I’ve set up in the example repository https://github.com/Tehnix/template-mobile-wasm.
We've set up four crates:
appy
: Our Leptos App, Capacitor, and the XCode projectcapacitor-rs
: Bridging code between the Capacitor JS library and our Rust codeshared
: Our shared code that we might use in appy
, and also want to expose in Swift to use in our Widgets or watchOS Appmobile
: Where we will generate the Swift bindings from via UniFFI, reexporting everything from shared
that’s made available to UniFFI via the macrosIn this post we'll focus on the capacitor-rs
and appy
crates, which contain our Capacitor bridge and our app where we'll use it respectively.
An overview of what we'll cover:
capacitor-rs
First, let’s create a new library where we’ll keep the Capacitor bridge related code in:
We’ll also initialize an empty TypeScript project using bun which we’ll use to install the Capacitor dependencies and bundle our TypeScript files:
)
)
We’ll then create a few new folders to host our code in (assuming you’re in the ./capacitor-rs
directory):
# We'll expose an interface for Capacitor from JS here.
# We'll keep our bundled JS files here.
# We'll keep the corresponding Rust files here.
Let’s set up our capacitor-rs/src/lib.rs
to expose the plugins folder:
1
And finally, let’s update our dependencies of the Rust project in ./capacitor-rs/Cargo.toml
:
1 []
2 = "capacitor-rs"
3 = "0.1.0"
4 = "2021"
5
6 []
7 # Match Trunk's wasm-bindgen version for compatability.
8 = { = "=0.2.87" }
9 # Used for working with async/promises in JS.
10 = { = "0.4" }
11 = { = "0.3" }
For this example we’ll set up the Haptics plugin, which will allow us to provide tactile feedback from our App in response to user interactions.
Our approach will be:
./appy
and ./capacitor
projects ./appy
: Capacitor needs access to the plugins when packaging our App./capacitor-rs
: We’ll need access to the libraries to be able to bundle them with bun into a single file since wasm_bindgen
does not support importswasm_bindgen
proc macro to create a Rust interface for the TypeScript functions we’ve definedLet’s add the TypeScript side of things in ./js/haptics.ts
which quite simply just wraps and exposes the Capacitor functionality:
1 /**
2 * https://capacitorjs.com/docs/apis/haptics
3 */
4 ;
5 ;
6
7 8 9 ;
10
11 12 13 ;
14
15 16 17 ;
18
19 20 21 ;
22
23 24 25 ;
26
27 28 29 ;
30
31 32 33 ;
We then bundle this with bun:
bun build --target browser --minify --outdir js-dist js/**
Which gives us a corresponding bundled file in js-dist/haptics.js
that we can use.
As the final step, we’ll bridge this into our Rust code by setting up src/plugins/haptics.rs
:
1 use *;
2
3
4 5 6 7 8 9 10 11 12
13
14
15
16 17 18 19 20 21 22 23 24 25 26 27 28 29
30
31 32 33 34 35 36 37 38 39
40
41
42 extern "C" 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
Let’s break down what’s going on here:
ImpactOptions
, ImpactStyle
(unfortunately wasm_bindgen
doesn’t support generating these for us, but it’s more or less a copy-paste of the TypeScript code)#[wasm_bindgen(module = "/js-dist/haptics.js")]
which will ensure it gets included automaticallyWe also need to add a src/plugins/mod.rs
file to expose our new plugin:
1
We’re now ready to use it in our App by adding the new capacitor-rs
crate to our dependencies in our WASM app:
1 # ...
2 []
3 = { = "0.1.0", = "../capacitor-rs" }
4 # ...other dependencies
And then using it like you like a normal Rust function:
1 use info;
2 use haptics;
3 use *;
4
5
6
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
And that’s it!
Since I can’t exactly screenshot a haptic vibration, here’s an example where we use the Capacitor utility function to determine which platform we are on, set up in the same way: