Building cross-platform native desktop apps in
Agenda
- Revery - overview & under the hood
- React & Revery
- Package manager & external libraries
- Using C/C++ libraries
- OCaml threads
- Demo & code walkthrough
- Why desktop cross-platform apps
- Electron - most popular framework for desktop
- Why not Electron
- Intro
Example apps
- Code editors
- Chat applications
- Web browsers
- Microsoft Office applications
Desktop app
- Works offline
- Local resources (file system, hardware)
- OS features (notifications, registry, launch on startup)
- Much faster than web apps
Build for each OS *
Web inside desktop
- Use JS, HTML, CSS
- Runs inside chromium (Chrome, Edge and Opera)
- 270,025 downloads per week
- An app becomes a web-browser
- High memory and CPU usage
- High bundle size
Revery
- Alternative to electron
- Cross-platform
- Compiles to native code (assembly code)
- Super fast & instant startup
- Functional code with OCaml/Reason
- Still work-in-progress
- Started from Onivim2 code editor
Revery
- Built with ReasonML
- Uses C/C++ libraries with OCaml/Reason bindings
* Under the hood
- SKIA for drawing - 2D graphics library in C++ (graphics engine of Google Chrome)
- SDL2 (Simple DirectMedia Layer) - access to audio, keyboard, mouse and graphics hardware, written in C
Revery & React
- React UI model
- UI = function(State)
- Components
- Hooks
- Built-in components: Checkbox, Button, View etc.
- React concepts: props, state, children
- Hooks: effect, reducer, state
Component
let make = (~onClick, ()) => {
<View style=Style.[alignItems(`Center)]>
<Text
style=Style.[fontSize(20)]
text="Do eeeet"
/>
<Button width=200 title="Go" onClick />
</View>
}
State & reducer
type state = {count: int};
type action =
| Increment
| Decrement;
let reducer = (action, state) => {
switch (action) {
| Increment => {count: state.count + 1}
| Decrement => {count: state.count - 1}
};
};
State & reducer
let%component make = () => {
let%hook (state, dispatch) =
Hooks.reducer(~initialState={count: 0}, reducer);
let onPlus = _ => dispatch(Increment);
let onMinus = _ => dispatch(Decrement);
<View>
<Button onClick={onPlus} title="+" />
<Button onClick={onMinus} title="-" />
</View>;
};
Package manager
-
npm for JavaScript libraries
- package.json, package-lock.json
- node_modules
-
esy for native Reason/OCaml libraries
-
npm and opam (OCaml) registries
- package.json / esy.json, esy.lock
-
Libraries
- install esy package hosted on npm
- install OCaml package hosted on opam
npm install -g esy
esy add my-lib
esy add @opam/my-lib
- C/C++ library - write OCaml bindings
Using C++ library
OCaml/Reason bindings
#include "audio.h"
CAMLprim value SDL_playMusicCAML(value name, value volume)
{
CAMLparam2(name, volume);
const char *filename = String_val(name);
playMusic(filename, volume);
CAMLreturn(Val_unit);
}
threads
- OCaml run-time can execute only one thread at a time
- Runtime lock prevents threads from running parallel
- Parallelism is achieved for system calls and C code
- Revery runs render loop
- Blocking calls to C code will freeze the UI
-
Release and re-acquire the lock,
caml_release_runtime_system(); caml_acquire_runtime_system();
DEMO
State machine visualization: https://xstate.js.org/viz/?gist=731a6bcc9291fe38e7edabdca3ce43ef
Negatives
- No hot reloading
- Debugging with print_endline into console
- Limited support for windows (code editor, installer etc.)
- Missing implementation for native features (coming soon!)
Quick start
Clone revery-quick-start, https://github.com/revery-ui/revery-quick-start
esy install
esy build
esy run
Tack!!!
Revery
By margaretkru
Revery
- 257