Naming conventions
Identifiers are translated mechanically between Rust and each target language. The table below shows the convention applied to each identifier category.| Item | Rust | Swift | Kotlin | TypeScript |
|---|---|---|---|---|
| Methods / Functions | snake_case | camelCase | camelCase | camelCase |
| Variables / Arguments | snake_case | camelCase | camelCase | camelCase |
| Classes / Interfaces | PascalCase | PascalCase | PascalCase | PascalCase |
mls_init becomes mlsInit in Swift, Kotlin, and TypeScript. The Rust struct CoreCryptoCallbacks remains CoreCryptoCallbacks in all target languages.
Type equivalence
The table below covers every primitive and composite Rust type exposed through the FFI. Types not listed here (custom structs, enums) are generated as equivalent classes or enums in the target language with the naming convention above applied.| Rust | Swift | Kotlin | TypeScript |
|---|---|---|---|
bool | Bool | Boolean | boolean |
u8 | UInt8 | UByte | number |
u16 | UInt16 | UShort | number |
u32 | UInt32 | UInt | number |
u64 | UInt64 | ULong | number |
i8 | Int8 | Byte | number |
i16 | Int16 | Short | number |
i32 | Int32 | Int | number |
i64 | Int64 | Long | number |
f32 | Float | Float | number |
f64 | Double | Double | number |
String / &str | String | String | string |
std::time::SystemTime | Date | java.time.Instant | Date |
std::time::Duration | TimeInterval | java.time.Duration | number (milliseconds) |
Option<T> | Optional<T> | Optional<T> | T? |
Vec<T> | Array<T> | List<T> | Array<T> |
HashMap<String, T> | Dictionary<String, T> | Map<String, T> | Record<string, T> |
() | nil | null | null |
Result<T, E> | func f() throws E -> T | fun f(): T // throws E | function f(): T // @throws E |
TypeScript represents all integer and floating-point Rust types as
number. There is no separate integer type in TypeScript. Values larger than Number.MAX_SAFE_INTEGER (i.e., u64/i64 near their limits) may lose precision.std::time::Duration maps to a plain number in TypeScript representing milliseconds, not seconds.UniFFI-generated bindings (Swift and Kotlin)
Swift and Kotlin bindings are auto-generated by UniFFI from the Rust source incrypto-ffi/. UniFFI reads the #[uniffi::export] annotations on Rust functions, traits, and structs, then emits matching Swift and Kotlin source files.
The generation is driven by two configuration files:
crypto-ffi/uniffi.toml— used for Swift and JVM Kotlin. Sets the package name (com.wire.crypto), the shared library name (core_crypto_ffi), and a customTimestamptype mapping tokotlinx.datetime.Instant.crypto-ffi/uniffi-android.toml— used for Android Kotlin. Enables the Android-specific object cleaner and mapsKotlinInstanttokotlinx.datetime.Instant.
Vec<u8> in Rust will accept Data in Swift and ByteArray in Kotlin. No manual wrapping is required.
Async patterns
Rustasync fn functions are translated as follows:
- Swift
- Kotlin
Rust async functions become native Swift
async functions. Call them with await inside an async context or a Task.wasm-bindgen / UBRN bindings (TypeScript)
TypeScript bindings for the browser target are generated from Rust code annotated with#[wasm_bindgen], compiled to wasm32-unknown-unknown, and processed with wasm-bindgen. The native (Node.js) target uses uniffi-bindgen-react-native (ubrn) to generate N-API bindings from the same UniFFI-annotated Rust source used for Swift and Kotlin.
Both targets are configured via crypto-ffi/bindings/js/ubrn.config.yaml and produce a single @wireapp/core-crypto package with two entry points:
@wireapp/core-crypto/browser— WASM build for web browsers@wireapp/core-crypto/native— N-API build for Node.js
Async patterns
All async Rust functions becomePromise-returning TypeScript functions. Use await or .then() as normal.
Error handling
Result<T, E> on the Rust side propagates as a thrown error in all three binding languages.
- Swift
- Kotlin
- TypeScript
Failable functions are marked The thrown type is
throws in the generated signature. Catch errors with do/catch.CoreCryptoError, which wraps MlsError and ProteusError variants.