• Stars
    star
    1,727
  • Rank 26,999 (Top 0.6 %)
  • Language
    Rust
  • License
    Apache License 2.0
  • Created about 2 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Cross-platform app development in Rust

Crux Β· GitHub license Crate version Docs Build status

Cross-platform app development in Rust

  • Shared Core for Behavior - Crux helps you share your app's business logic and behavior across mobile (iOS/Android) and web β€” as a single reusable core built with Rust.
  • Thin Shell for UI - Crux recognizes that the best experiences are built with modern declarative frameworks such as SwiftUI, Jetpack Compose, React/Vue, or a WebAssembly based framework (like Yew) β€” however, it aims to keep this UI layer as thin as it can be, with all other work done by the shared core.
  • Type Generation - the interface with the core has static type checking across languages β€” types and serialization code are generated for Swift, Kotlin and TypeScript. Rust shells can import the core directly.
  • Capabilities - capabilities express the intent for side effects such as calling an API. Because all side effects (including UI) are performed by the shell, the core becomes trivial to test comprehensively β€” test suites run in milliseconds (not in minutes or hours).

Getting Started

Learn how to use Crux in your project.

Follow the readme in the project's repository on Github.

Read the API documentation

Watch the introductory talk at the recent Rust Nation 2023 conference in London.

You can also join the friendly conversation on our Zulip channel.

Note, that Crux is experimental and currently under active development (probably not ready for use in production apps just yet). However, the master branch should always be working well, and we will try to keep the examples and documentation up to date as we go. We do think that the API has now settled, so have a play! :-)

Architectural Overview

Logical architecture

The fundamental architectural concept is the strict separation of pure computational tasks from tasks that cause side effects. This is similar to the way Elm works.

Side-effect-free core

In the above diagram, the inner "Core" is compiled and linked to the outer "Shell" on each platform as a library:

  • On iOS as a native static library
  • On Android as a dynamic library using Java Native Access
  • In a browser as a WebAssembly module

In fact, because WebAssembly (Wasm) is one of the compilation targets, the core must remain side-effect free, due to the sandboxed nature of the Wasm runtime environment.

As such, the core is completely isolated and secure against software supply-chain attacks, as it has no access to any external APIs. All it can do is perform pure calculations and keep internal state.

Following the Elm architecture, the core defines the key component types within the application:

  • Event β€” an enum describing the events which the core can handle
  • Model β€” describes the internal state of the application
  • ViewModel β€” represents information that should be displayed to the user

The former two are tied together by the update function, familiar from Elm, Redux or other event sourcing architectures, which currently has this type signature:

fn update(
    &self,
    event: Event,
    model: &mut Model,
    capabilities: &Capabilities,
)

The job of the update function is to process an Event, update the model accordingly, and potentially request some side-effects using capabilities.

Application Shell

The enclosing platform native "Shell" is written using the language appropriate for the platform, and acts as the runtime environment within which all the non-pure tasks are performed. From the perspective of the core, the shell is the platform on which the core runs.

Communication Between the Application Shell and the Core

Following the Elm architecture, the interface with the core is message based. This means that the core is unable to perform anything other than pure calculations. To perform any task that creates a side-effect (such as an HTTP call or random number generation), the core must request it from the shell.

The core has a concept of Capabilities β€” reusable interfaces for common side-effects with request/response semantics. There are already a few embryonic Capability crates (Http, KeyValue, Time, Platform, and the builtin Render) β€” and you can write your own if you need/want to.

crux

This means the core interface is simple:

  • process_event: Event -> Vec<Request> - processes a user interaction event and potentially responds with capability requests. This is the API for the driving side in the above diagram.
  • handle_response: (uuid, SomeResponse) -> Vec<Request> - handles the response from the capability and potentially follows up with further requests. This is the API for the driven side in the above diagram.
  • view: () -> ViewModel - provides the shell with the current data for displaying user interface

Updating the user interface is considered a side-effect and is provided by the built-in Render capability.

This design means the core can be tested very easily, without any mocking and stubbing, by simply checking the Input/Output behaviour of the three functions.

Foreign Function Interface

The Foreign Function Interface allowing the shell to call the above functions is provided by Mozilla's UniFFI on a mobile device, or in the browser, by wasm-pack.

In order to both send more complex data than UniFFI currently supports, and enforce the message passing semantics, all messages are serialized, sent across the boundary, then deserialized using serde-generate which also provides type generation for the foreign (non-Rust) languages.

This means that changes to types in the core, especially the Event and Request types, propagate out into the shell implementations and cause type errors where appropriate (such as an exhaustive match on an enum check).

Message Types

Three types of message are exchanged between the application and the core.

  • Messages of type Event are sent from the Shell to the Core in response to an event happening in the user interface (the driving side). They start a potential sequence of further message exchanges between the shell and the core. Messages are passed on unchanged.
  • Messages of type Request are sent from the Core to the Shell to request the execution of some side-effect-inducing task. The Core responds with zero or more Request messages after receiving an Event message (the driven side).
  • Response messages are sent from the Shell to the Core carrying the result of an earlier request.

Request messages contain the inputs for the requested side-effect, along with a uuid used by the core to pair requests and their responses together. The exact mechanics are not important, but it is important for the request's uuid to be passed on to the corresponding response.

Typical Message Exchange Cycle

A typical message exchange cycle may look like this:

  1. User interaction occurs in the Shell, which results in an event
  2. The Shell handles this event by constructing an Event
  3. The Shell calls the Core's process_event function passing the Event as an argument
  4. The Core performs the required processing, updating both its inner state and the view model
  5. The Core returns one or more Request messages to the Shell

In the simplest case, the Core will respond to an Event by returning the single Request - render.

This requests that the Shell re-renders the user interface. When Render is the only response from the Core, the message cycle has completed and the Core has now "settled".

In more complex cases however, the Core may well return multiple Requests; each of which instructs the Shell to perform a side-effect-inducing task such as:

  • Make a network call, or
  • Fetch the current date/time stamp, or
  • Perform biometric authentication, or
  • Obtain an image from the camera, or
  • Whatever else you can think of...

Many of these side-effecting-inducing tasks are asynchronous. The Shell is responsible for passing responses back to the core (to the handle_response function), which may respond with further requests.

This exchange continues until the core stops requesting further side-effects (typically the last side-effect requested would be Render).

Run the Counter Example locally

Refer to examples/counter README

How to Start Your Own New Project

Refer to the Getting Started section of the tutorials.


Sponsors

Crux is kindly sponsored by the following organizations. Your help is very much appreciated.


Red Badger Consulting Limited

Red Badger logo

Red Badger is the digital product consultancy trusted by blue chips and global brands. Our product design and technical pedigree allow us to craft high-impact digital products customers want. We use modern engineering approaches to deliver sustainable change. And embed digital capabilities to power continuous innovation.


Zulip

Zulip round icon

Zulip is an open-source modern team chat app designed to keep both live and asynchronous conversations organized.

Zulip sponsor Crux by providing our Zulip server β€”Β thank you Zulip!


More Repositories

1

pride-london-app

Pride in London mobile app
JavaScript
85
star
2

immutable-cursor

πŸ‘Š Immutable cursors incorporating the Immutable.js interface over a Clojure-inspired atom
JavaScript
59
star
3

react-isomorphic

LiveScript
37
star
4

Puppeteer-lighthouse-jest

A starter suite of tests utilising Puppeteer to drive non-functional tests through a Lighthouse audit
JavaScript
30
star
5

redux-mori

JavaScript
30
star
6

stack

Testing docker stack and swarm mode
JavaScript
27
star
7

website-honestly

πŸ¦„ The Red Badger website. Honestly.
JavaScript
26
star
8

rugged-redis

Redis storage backend for rugged
Ruby
23
star
9

rpi-wasmcloud-demo

Demo running wasmcloud lattice on MacOS and 2x Raspberry Pi 4B
Rust
22
star
10

XPF

XPF - a layout framework for XNA on Windows & Windows Phone 7 from Red Badger Consulting
C#
21
star
11

wasmcloud-k8s-demo

Demo of deploying a wasmcloud cluster across multiple cloud providers
Shell
18
star
12

feature-targeting

Feature targeting system for K8s/Istio
Rust
18
star
13

flits

Web based control panel for coreos/fleet
Go
16
star
14

badger-brain

GraphQL server for all Red Badger related data
JavaScript
13
star
15

QUnitTeamCityDriver

Allows TeamCity to execute QUnit JavaScript unit tests using PhantomJS
JavaScript
12
star
16

raw_wasm

WebAssembly modules written directly in WebAssembly text
HTML
11
star
17

karma-tracker

Open Source contributions tracker for organisations
Clojure
10
star
18

react.london

🌟 react.london conference & community website 🌟
SCSS
10
star
19

awesome-badger

Assorted thinking (mostly) about technology written by the Badgers.
WebAssembly
10
star
20

test-report-junit-xml

JUnit XML output for clojure.test
Clojure
9
star
21

test-report

Test reporting for clojure.test
Clojure
9
star
22

microplatforms-whitepaper

Microplatforms white paper LaTeX source (work in progress)
TeX
6
star
23

website-next

DEPRECATED incarnation of Red Badger company site
JavaScript
6
star
24

react-pillbox

Pillbox component for React
JavaScript
4
star
25

webpagetest-aws-terraform

Complete AWS infrastructure setup of the WebPagetest AMIs using Terraform
HCL
4
star
26

square-the-circle

CircleCI stats reporter
JavaScript
4
star
27

CloudFolderBackup

Über simple app that takes a folder, zips it and uploads it to Rackspace Cloud Files
C#
4
star
28

clojure-gol

Game of Life in Clojure
Clojure
4
star
29

badger-map

Where do badgers live?
HTML
3
star
30

badger-brian

Dapr(y) version of Badger Brain
TypeScript
3
star
31

monorepo-examples

3
star
32

oss-contribution-tracker

Tracks open source contributions by organisation members over time
Clojure
3
star
33

pathogen

Typed references and deep mutation into Rust types
Rust
3
star
34

decoder-ring

A secret decoder ring is a device which allows one to decode a secret message
JavaScript
2
star
35

docker-credential-ecr-login

Amazon ECR Docker Credential Helper as a Docker image
2
star
36

radiobadger.com

Official site of the Radio Badger podcast. Live at www.radiobadger.com
CSS
2
star
37

deploy

Deploy to Kubernetes through a Github repo
Go
2
star
38

vagrant-rails-elasticsearch

Vagrant development environment for rails and elasticsearch, using ansible for provisioning.
Shell
2
star
39

noflo-freeagent

NoFlo FreeAgent Library
CoffeeScript
2
star
40

PostmanPOC

POC for storing a Postman collection and env variables in GitHub
1
star
41

rust-aoc-2022

Rust implementations of Advent of Code 2022 for learning purposes
Rust
1
star
42

count-dracula

A mock NHS blood donation checklist being used to test continuous compliance
TypeScript
1
star
43

dynamic-form

Dynamic form library using redux-form
JavaScript
1
star
44

blog-squarespace-template

Squarespace template for a Red Badger company blog
CSS
1
star
45

kingfisher-converter

Converts the CSV file to a json file
JavaScript
1
star
46

facereplace

Taking faces and replacing them with emojis
JavaScript
1
star
47

react-immutable-cursor

A React helper allowing for intuitive bidirectional data flow, through the use of cursors into a single, immutable root atom
JavaScript
1
star
48

build-with-cache

Docker multi-stage build with layer caching
Go
1
star
49

md-to-couch

Convert a bunch of markdown files into CouchDB compatible JSON
JavaScript
1
star
50

ScriptDependencyResolver

Resolves dependencies between JavaScript files based on declared reference paths
C#
1
star
51

monorepo

Trialling a variety of CI services
1
star
52

jquery-xdr

Bower component to allow CORS in IE8/9 via jQuery
CoffeeScript
1
star
53

continous-compliance

GitHub Action that gather compliance evidence and store it in the cloud
TypeScript
1
star
54

jams

Tool for collaborative surveys, measuring and creating consensus.
JavaScript
1
star
55

websites-guide

User documentation on maintaining Red Badger public websites
CSS
1
star
56

wasm-js-perf

Various programs that compare WASM and JS performance
JavaScript
1
star
57

react.london-snapshot

Static snapshot of react.london and meetup.react.london websites
HTML
1
star
58

nats-server-runner

Run the NATS messaging system server - Useful for running unit or integration tests on the localhost
Kotlin
1
star