Hekate Swift
On-device zero-knowledge proofs on iOS: one async Swift call runs your Rust prover in a detached task, and Task.cancel() reaches Rust.
On this page
Async / cancellation facade over the UniFFI-generated Swift bindings. Maps Swift Concurrency (Task, TaskPriority,
structured cancellation) onto the synchronous prove(inputs:cancel:) C-ABI call that the prover crate exports through
UniFFI.
The wrapper is ~30 lines. Two jobs and nothing more: run the multi-second prove in a detached task, which keeps the
caller's actor free, and route Task.cancel() through the FFI CancelToken to make a cooperative cancel actually reach Rust.
§ Installation
Package.swift lives at the repository root. Add the package by repo URL:
dependencies: [
.package(url: "https://github.com/oumuamua-labs/hekate-mobile.git", from: "<version>"),
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "Hekate", package: "hekate-mobile"),
]
)
]
<version> is the release in the line under the title. SwiftPM names a remote package after the last component of its
URL, which makes the product's package hekate-mobile.
The package ships only the HekateProver and CancellableToken protocols and the async extension. cargo xtask ios-build
writes MyProver.xcframework, HekateProverCdylib.xcframework and the Swift bindings file. The app embeds both frameworks
and declares the prover type (MyProver) once over the bindings, as the
package README shows.
§ Usage
import Hekate
let output = try await MyProver.prove(
inputs: myInputs,
priority: .userInitiated
)
That's the surface. No cancel tokens, no completion handlers, no DispatchQueue.global().async { ... }, no JNI-style
typed-buffer plumbing. The try covers every failure mode the Rust prover can produce, the await suspends the caller
until the proof is ready or the parent task is cancelled.
priority is forwarded to the detached Task that runs the synchronous FFI call, which occupies one thread of Swift's
cooperative pool until it returns. .background is the right choice for batch or off-screen proving. .userInitiated
is acceptable for interactive flows but risks the iOS background-CPU killer if the app suspends mid-prove.
§ Cancellation
Task.cancel() on the enclosing Swift task automatically calls CancelToken.request() through
withTaskCancellationHandler. That covers every form of cancellation Swift Concurrency produces:
- parent-driven structured cancellation when a
TaskGroupaborts, - view-lifecycle cancellation in SwiftUI when the host view disappears,
- an explicit
cancel()from a coordinator orTaskhandle.
The Rust prover stops at its next poll of the token, and the await throws ProveError.Cancelled. There is no manual
cancel-token plumbing on the call site, and there is no Thread.cancel() analogue in Swift to misuse.
The cancellation propagation is cooperative on both sides: Rust observes the flag at its next poll and returns. The witness build takes no token, and a cancel that arrives during it is not seen until the prover runs.
§ Panic isolation
The Rust prove entry point wraps the prover invocation in catch_unwind(AssertUnwindSafe(...)). If the prover panics
for any reason, debug assertion, integer overflow in debug_assertions, a bug in the dev's circuit or witness builder,
catch_unwind captures it, formats the panic payload, and returns Err(ProveError::Panic(message)). UniFFI maps that
variant to a typed Swift enum ProveError case:
do {
let output = try await MyProver.prove(inputs: myInputs)
} catch ProveError.Panic(let message) {
// log + surface; the prove call did NOT corrupt your process
} catch ProveError.Cancelled {
// user-initiated or structured cancel
} catch {
// every other typed prover error variant
}
No abort() crosses the FFI boundary. The Rust panic unwinds locally inside catch_unwind, the FFI return frame is
intact, and the Swift caller sees a typed error instead of a SIGABRT on the next stack frame.
§ Next steps
- Your First ZK Program: write the statement in Rust, a private payment proved and verified end to end.
- Preflight: check a witness against every constraint before the prover runs.
- Verifier Logic: verify the proof on a server and pin the program id.
- Soundness and Security: what a proof guarantees and the checks left to you.