{"id":13405785,"url":"https://github.com/redbadger/crux","last_synced_at":"2025-05-13T00:32:14.074Z","repository":{"id":62303853,"uuid":"547831094","full_name":"redbadger/crux","owner":"redbadger","description":"Cross-platform app development in Rust","archived":false,"fork":false,"pushed_at":"2025-04-18T14:35:10.000Z","size":10270,"stargazers_count":1912,"open_issues_count":34,"forks_count":74,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-19T02:21:56.924Z","etag":null,"topics":["mobile-development","rust"],"latest_commit_sha":null,"homepage":"https://redbadger.github.io/crux/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/redbadger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-10-08T11:30:56.000Z","updated_at":"2025-04-18T16:13:28.000Z","dependencies_parsed_at":"2022-10-30T11:15:56.108Z","dependency_job_id":"71b426b4-abd1-453b-a897-114aa89d1963","html_url":"https://github.com/redbadger/crux","commit_stats":{"total_commits":619,"total_committers":10,"mean_commits":61.9,"dds":"0.43941841680129246","last_synced_commit":"1c00990a4e43dcedc2ce6b2fd2b2dc85b2ef2843"},"previous_names":[],"tags_count":183,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redbadger%2Fcrux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redbadger%2Fcrux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redbadger%2Fcrux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redbadger%2Fcrux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redbadger","download_url":"https://codeload.github.com/redbadger/crux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250496397,"owners_count":21440227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mobile-development","rust"],"created_at":"2024-07-30T19:02:11.562Z","updated_at":"2025-04-23T18:52:38.722Z","avatar_url":"https://github.com/redbadger.png","language":"Rust","funding_links":[],"categories":["Rust","Libraries","rust","GUI"],"sub_categories":["Mobile"],"readme":"# [Crux](https://red-badger.com/crux) \u0026middot; [![GitHub license](https://img.shields.io/github/license/redbadger/crux?color=blue)](https://github.com/redbadger/crux/blob/master/LICENSE) [![Crate version](https://img.shields.io/crates/v/crux_core.svg)](https://crates.io/crates/crux_core) [![Docs](https://img.shields.io/badge/docs.rs-crux_core-green)](https://docs.rs/crux_core/) [![Build status](https://img.shields.io/github/actions/workflow/status/redbadger/crux/build.yaml)](https://github.com/redbadger/crux/actions)\n\n\u003ca href=\"https://red-badger.com/crux\"\u003e\u003cimg src=\"./crux_core/crux_logo.svg\" height=\"100\" /\u003e\u003c/a\u003e\n\n**[Watch the introductory talk](https://www.youtube.com/watch?v=cWCZms92-1g\u0026t=5s) | [Read the book](https://redbadger.github.io/crux) | [Read API docs](https://docs.rs/crux_core/latest/crux_core/) | [Join Zulip community](https://crux-community.zulipchat.com)**\n\n## Cross-platform app development in Rust\n\n- **Shared Core for Behavior** - Crux helps you share your app's business logic\n  and behavior across mobile (iOS/Android) and web — as a single reusable core\n  built with Rust.\n- **Thin Shell for UI** - Crux recognizes that the best experiences are built\n  with modern declarative frameworks such as\n  [SwiftUI](https://developer.apple.com/xcode/swiftui/),\n  [Jetpack Compose](https://developer.android.com/jetpack/compose),\n  [React](https://reactjs.org/)/[Vue](https://vuejs.org/), or a WebAssembly\n  based framework (like [Leptos](https://leptos.dev/), or\n  [Yew](https://yew.rs/)) — however, it aims to keep this UI layer as thin as it\n  can be, with all other work done by the shared core.\n- **Type Generation** - the interface with the core has static type checking\n  across languages — types and serialization code are generated for Swift,\n  Kotlin and TypeScript. Rust shells can import the core directly.\n- **Managed effects** - Side effects such as calling an API are captured as values\n  and executed by the Shell. The core stays side-effect free, making it portable\n  across platforms and allowing high-level user journey tests to run in milliseconds\n  (rather than minutes or even hours)\n\n## Getting Started\n\n[Learn how to use Crux in your project](https://redbadger.github.io/crux).\n\nRead the [API documentation](https://docs.rs/crux_core/latest/crux_core/)\n\n[Watch the introductory talk](https://www.youtube.com/watch?v=cWCZms92-1g\u0026t=5s)\nat the recent [Rust Nation 2023](https://www.rustnationuk.com/) conference in\nLondon.\n\nYou can also join the friendly conversation on our\n[Zulip channel](https://crux-community.zulipchat.com).\n\n\u003e [!NOTE]\n\u003e Crux is pre 1.0 and under active development. It is production-ready, but\n\u003e occasional breaking changes to the API can be expected. We do our best to\n\u003e limit the extent of these and provide a smooth, gradual migration path\n\n## Architectural Overview\n\n![Logical architecture](./crux_core/architecture.svg)\n\nCrux has managed side-effects, it strictly separates pure computational tasks from tasks that\ncause side effects. This is similar to the way [Elm](https://guide.elm-lang.org/architecture/) works.\n\n### Side-effect-free core\n\nIn the above diagram, the inner \"Core\" is compiled and linked to the outer\n\"Shell\" on each platform as a library:\n\n- On iOS as a native static library\n- On Android as a dynamic library using\n  [Java Native Access](https://github.com/java-native-access/jna)\n- In a browser as a WebAssembly module\n\nIn fact, because WebAssembly (Wasm) is one of the compilation targets, the core\n_must_ remain side-effect free, due to the sandboxed nature of the Wasm runtime\nenvironment.\n\nAs such, the core is completely isolated and secure against software\nsupply-chain attacks, as it has no access to any external APIs. All it can do is\nperform pure calculations and keep internal state.\n\nFollowing the Elm architecture, the core defines the key component types within\nthe application:\n\n- `Event` — an `enum` describing the events which the core can handle\n- `Model` — describes the internal state of the application\n- `Effect` – the kinds of side-effects the core will request\n- `ViewModel` — represents information that should be displayed to the user\n\nThe first three are tied together by the `update` function, familiar from Elm,\nRedux or other event sourcing architectures, which currently has this type\nsignature:\n\n```rust,ignore\nfn update(\n    \u0026self,\n    msg: Event,\n    model: \u0026mut Model,\n    _caps: \u0026Capabilities, // soon to be deprecated\n) -\u003e Command\u003cEffect, Event\u003e {\n    // ...\n}\n```\n\nThe job of the `update` function is to process an `Event`, update the model\naccordingly, and potentially request some side-effects.\n\n\u003c!--prettier-ignore--\u003e\n\u003e [!NOTE]\n\u003e The `Capability` API is being deprecated in favour of a more flexible `Command` API.\n\u003e\n\u003e To learn more about the new [`Command` API](https://docs.rs/crux_core/latest/crux_core/command/index.html),\n\u003e see the page in the book that describes [Managed Effects](https://redbadger.github.io/crux/guide/effects.html),\n\u003e or look at [examples/counter](https://github.com/redbadger/crux/tree/master/examples/counter).\n\n### Application Shell\n\nThe enclosing platform native \"Shell\" is written using the language appropriate\nfor the platform, and acts as the runtime environment within which all the\nnon-pure tasks are performed. From the perspective of the core, the shell is the\nplatform on which the core runs.\n\n### Testing\n\nTests can act as another Shell, exercising the Core in the same way a real app would,\nobserving and resolving the requested effects and checking the model and view model\nare correct. No need for fakes, mocks or stubs.\n\n## Communication Between the Application Shell and the Core\n\nFollowing the Elm architecture, the interface with the core is message based.\nTo perform any task that creates a side-effect (such as an HTTP\ncall or random number generation), the core must request it from the shell as an `Effect`.\nEffects support fire-and-forget, request/response, and streaming semantics.\n\nCrux has a concept of Capabilities — reusable interfaces for common\nside-effects which can be used in the Core as a more ergonomic API.\n\nThe only built-in capability is `Render`. But this repository contains a few\ncapabilities at various stages of maturity, and you can easily write your own if\nyou want to:\n\n![crux](https://github.com/redbadger/crux/raw/master/docs/src/crux.png)\n\n### Published capabilities\n\n1. `Render` (ask UI to render the ViewModel) —\n   [source](./crux_core/src/capabilities/render.rs), built-in to `crux_core`,\n   request only\n1. `Http` (full HTTP implementation based on the\n   [Surf](https://crates.io/crates/surf) API) — [source](https://github.com/redbadger/crux/tree/master/crux_http),\n   [crate](https://crates.io/crates/crux_http), request/response\n1. `KeyValue` (basic key-value store API) — [source](https://github.com/redbadger/crux/tree/master/crux_kv),\n   [crate](https://crates.io/crates/crux_kv), request/response\n1. `Time` (get current time, notify after duration, notify at instant) —\n   [source](https://github.com/redbadger/crux/tree/master/crux_time), [crate](https://crates.io/crates/crux_time),\n   request/response\n1. `Platform` (get the current platform) — [source](https://github.com/redbadger/crux/tree/master/crux_platform),\n   [crate](https://crates.io/crates/crux_platform), request/response\n\n### Example custom capabilities\n\n1. `SSE` (basic Server-Sent Events) —\n   [source](https://github.com/redbadger/crux/tree/master/examples/counter/shared/src/capabilities/sse.rs),\n   request/streaming\n1. `PubSub` (pub sub with streaming) —\n   [source](https://github.com/redbadger/crux/tree/master/examples/notes/shared/src/capabilities/pub_sub.rs),\n   request/response/streaming\n1. `Delay` — part of\n   [tutorial](https://redbadger.github.io/crux/guide/capability_apis.html#basic-delay-capability)\n   in the [book](https://redbadger.github.io/crux)\n\n### Foreign Function Interface with type generation\n\nThe core API interface is very minimal:\n\n- `process_event: Event -\u003e Vec\u003cRequest\u003e` - processes a user interaction event\n  and potentially responds with capability requests. This is the API for the\n  _driving_ side in the above diagram.\n- `handle_response: (uuid, SomeResponse) -\u003e Vec\u003cRequest\u003e` - handles the response\n  from the capability and potentially follows up with further requests. This is\n  the API for the _driven_ side in the above diagram.\n- `view: () -\u003e ViewModel` - provides the shell with the current data for\n  displaying user interface\n\n\nThe Foreign Function Interface allowing the shell to call the above functions is\nprovided by Mozilla's [UniFFI](https://mozilla.github.io/uniffi-rs/) on a mobile\ndevice, or in the browser, by\n[wasm-pack](https://rustwasm.github.io/wasm-pack/).\n\nIn order to both send more complex data than UniFFI currently supports, and\nenforce the message passing semantics, all messages are serialized, sent across\nthe boundary, then deserialized using\n[serde-generate](https://docs.rs/serde-generate/latest/serde_generate/) which\nalso provides type generation for the foreign (non-Rust) languages.\n\nThis means that changes to types in the core, especially the `Event` and\n`Request` types, propagate out into the shell implementations and cause type\nerrors where appropriate (such as an exhaustive match on an enum check).\n\n### Message Types\n\nThree types of message are exchanged between the application and the core.\n\n- Messages of type `Event` are sent from the Shell to the Core in response to an\n  event happening in the user interface (the _driving_ side). They start a\n  potential sequence of further message exchanges between the shell and the\n  core. Messages are passed on unchanged.\n- Messages of type `Request` are sent from the Core to the Shell to request the\n  execution of some side-effect-inducing task. The Core responds with zero or\n  more `Request` messages after receiving an `Event` message (the _driven_\n  side).\n- Response messages are sent from the Shell to the Core carrying the result of\n  an earlier request.\n\n`Request` messages contain the inputs for the requested side-effect, along with\na `id` used by the core to pair requests and their responses together. The\nexact mechanics are not important, but it is important for the request's `id`\nto be passed on to the corresponding response.\n\n## Example Message Exchange Cycle\n\nA typical message exchange cycle may look like this:\n\n1. User interaction occurs in the Shell, which results in an event\n1. The Shell handles this event by constructing an `Event`\n1. The Shell calls the Core's `process_event` function passing the `Event` as an\n   argument\n1. The Core performs the required processing, updating both its inner state and\n   the view model\n1. The Core returns one or more `Request` messages to the Shell (inside an enum\n   tagging the type of request)\n\nIn the simplest case, the Core will respond to an `Event` by returning the\nsingle `Request` - render.\n\nThis requests that the Shell re-renders the user interface. When `Render` is the\nonly response from the Core, the message cycle has completed and the Core has\nnow \"settled\".\n\nIn more complex cases however, the Core may well return multiple `Request`s;\neach of which instructs the Shell to perform a side-effect-inducing task such\nas:\n\n- Make a network call, or\n- Fetch the current date/time stamp, or\n- Perform biometric authentication, or\n- Obtain an image from the camera, or\n- Whatever else you can think of...\n\nMany of these side-effecting-inducing tasks are asynchronous. The Shell is\nresponsible for passing responses back to the core (to the `handle_response`\nfunction), which may respond with further requests.\n\nThis exchange continues until the core stops requesting further side-effects\n(typically the last side-effect requested would again be `Render`).\n\n---\n\n## Sponsors\n\nCrux is kindly sponsored by the following organizations. Your help is very much\nappreciated.\n\n---\n\n### Red Badger Consulting Limited\n\n\u003cimg src=\"https://github.com/redbadger/crux/raw/master/docs/src/images/RB_Screen_Logos_Artwork-02.svg\" alt=\"Red Badger logo\" height=\"40px\"/\u003e\n\n[Red Badger](https://red-badger.com/) is the digital product consultancy trusted\nby blue chips and global brands. Our product design and technical pedigree allow\nus to craft high-impact digital products customers want. We use modern\nengineering approaches to deliver sustainable change. And embed digital\ncapabilities to power continuous innovation.\n\n---\n\n### Zulip\n\n\u003cimg src=\"https://github.com/redbadger/crux/raw/master/docs/src/images/zulip-icon-circle.svg\" alt=\"Zulip round icon\" height=\"40px\"/\u003e\n\n[Zulip](https://zulip.com/) is an open-source modern team chat app designed to\nkeep both live and asynchronous conversations organized.\n\nZulip sponsor Crux by providing our\n[Zulip server](https://crux-community.zulipchat.com) — thank you Zulip!\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredbadger%2Fcrux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredbadger%2Fcrux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredbadger%2Fcrux/lists"}