Cookbook
Moving from UniFFI
You already have a Rust core. Undra adds what sits above the bindings: state the UI observes, a data layer, ports with fakes and a dev loop. The move is mostly attributes.
Three steps#
| Step | What moves | What it removes |
|---|---|---|
| L1 side by side | A new Undra crate beside your UniFFI one. Plain-data functions first: #[uniffi::export] becomes #[undra::api]. | Cancellation from either side, panics as typed values, a typed error for a mismatched library (UNI-3 to UNI-5). |
| L2 one feature | State a screen polls, or is pushed through callbacks, becomes a store; its fetches a query and mutations. | The state-sync glue, and the cache, retry and offline logic you did not have (UNI-1, UNI-9). |
| L3 whole domain | Platform services reached through callbacks become ports. | The test doubles you wrote by hand (UNI-12). |
Row ids are those of the limitations catalogue, as of October 2026. Undra vs UniFFI and Crux compares the two and says where UniFFI is ahead.
What maps to what#
| UniFFI | Undra |
|---|---|
#[uniffi::export] fn, async fn | #[undra::api] fn, async fn |
uniffi::Record, uniffi::Enum | #[undra::api] struct, enum (payloads in all three languages) |
uniffi::Error | #[undra::error] |
an Object with methods | #[undra::api] impl; a #[undra::store] if the UI observes it |
| a callback that pushes state | a signal the UI observes |
| a foreign trait for a platform service | a port (#[undra::port]), one per platform |
| a callback interface passed as an argument | a #[undra::callback] trait, taken as Arc<dyn Trait> |
What does not map yet#
- Open-ended generics (E0002). Generic records, enums, functions, methods, objects and stores cross as the instantiations you list; a type parameter with no list does not, and there are no native generic types.
- Other hosts. Undra targets SwiftUI, Compose, the web and React Native; UniFFI also serves Python and Ruby.
Objects, callbacks and newtypes map: an object crosses as a parameter or a return value, as itself (objects), a #[undra::callback] trait taken as Arc<dyn Trait> is an argument the core calls back (callbacks), and a one-field tuple struct crosses as the value it wraps and arrives as a type of its own (newtypes). L1 still starts with plain data, because it moves without touching the platform code. Several cores load side by side, so the rest can wait. Auth and offline-first show what L2 adds.