Making a Bare Mobile Application
This guide demonstrates how to build and run a mobile application using Bare and Expo.
We will be building an application that syncs data from the Pearpass desktop application using autopass and displaying a list of the passwords on mobile.
Project Dependencies
Gradle version 8.10.2
Java 23
Android SDK (>= 28), NDK
Project Setup
We will use the bare-expo
template to build our application. To get started, first clone it with git in a new directory:
Change to the project directory:
And install the dependencies we will need:
Directory Structure
bare-expo
is a basic template for getting started with Bare applications on mobile. To build a "Pear-end", where the peer-to-peer (P2P) code of the application is run, we will change the directory structure a bit adding a dedicated directory for the Pear-end.
Create a new directory called backend
. This is where we will store our Pear-end code that will be run by Bare.
Create a new file backend.mjs
in the backend
directory, we will use this file as the entry point for anything P2P. All Bare code should be used here.
Our React Native UI code will go in the existing app/index.tsx
file.
Building the Application
Building the UI
The app/index.tsx
file that came with the bare-expo
template serves as the entry point of the UI of the React Native app.
Replace the contents of app/index.tsx
with:
Building the Pear-end
Add the following code to backend/backend.mjs
:
Bundling the Pear-end
Now we need to create a bundle with all of our Pear-end code, dependencies and references to native addon libraries so that Bare has everything it needs to run our code.
To create the bundle we will use bare-pack
. Mobile apps link the native addons ahead of time, so we can target only the platform and use the --linked
option to use those linked libraries. You can learn more about bundling with bare-pack
here.
To create a bundle that targets both iOS and Android, run:
This will create a bundle and store it under the app
directory as app.bundle.mjs
which is imported by the UI.
Running the application
iOS
Android
[!IMPORTANT] You may experience problems running the app on an emulated Android device under QEMU due to https://github.com/holepunchto/libjs/issues/4. If you encounter crashes, try running the app on a real Android device instead.
Viewing error logs
Any React Native error will be directly printed to the terminal. Bare (Pear-end) errors will not appear directly in the terminal but they can be viewed using logcat
when running the app on Android.
To view logs while running on iOS on macOS use the built-in Console.app or the log
tool using the predicate subsystem == 'bare'
.
For more information on consuming logs, see the liblog
README.
Explanation
React Native uses Hermes as its Javascript engine by default. Hermes however does not support User Datagram Protocol (UDP) and the low level control needed for P2P applications. That's where Bare comes in. It provides simple ABI stable native bindings (libjs
) that the Pear platform stack takes advantage of so you can write Javascript once and run it everywhere.
app/index.tsx
app/index.tsx
In order to create a Bare thread, we import the Worklet
class from react-native-bare-kit
. This class is what we use to create a Bare thread and is where the Pear-end (P2P) code is run.
app.bundle
was created while bundling the Pear-end. This bundle contains all of our dependencies and the code from app/backend.mjs
in a single file. We import this bundle so we can pass it to the worklet as the source to run.
bare-rpc
provides a remote procedure call (RPC) on top of react-native-bare-kit
's inter-process communication (IPC) stream to communicate between the React Native UI and the Pear-end.
b4a
is a module for creating buffers using Uint8Array
s where Buffer
s are not available. We use this to decode the request data sent via the bare-rpc
from the Pear-end.
Creates a new worklet
object, ideally we should only create a single worklet.
Here we start our worklet with the bundle we imported in the previous step. The '/app.bundle'
is the filename for the bundle, bundle
is the bundled source that we imported and [Platform.OS, pairingInvite]
are the arguments to be passed to the Bare runtime. These arguments will be available to the Bare process as soon as it starts in Bare.argv[]
.
Once the worklet has started, we can use its IPC stream to communicate between the UI and Pear-end, but using an RPC on top of the IPC is often easier.
To that end, we create an RPC passing it the IPC stream and defining an onrequest
callback for when a request is received from the other end.
Inside the callback, we respond to the request by first checking which command we received and then processing its data. If you need to reply to a command, you can also req.reply(data)
.
backend/backend.mjs
backend/backend.mjs
Here we import all necessary modules and define IPC
getting it from BareKit
. BareKit and Bare are global in this file and available by default.
As previously mentioned, arguments passed to the worklet are available in the Bare.argv
property. So Bare.argv[0]
contains the first argument passed to the Bare worklet.
Start the pairing process with the other Autopass instance using the invite passed to the worklet. Checkout the autopass
README to learn more.
Create a new RPC request, and send it with 'data'
, this will be received by the UI in the RPC's onrequest
callback.
Last updated