Swift iOS App
Goal
Build a small Rust greeter library, generate Swift bindings with WeaveFFI, and call them from a SwiftUI iOS app running in the simulator.
Prerequisites
-
Rust toolchain (stable channel).
-
Xcode 15 or later with the iOS SDK installed.
-
WeaveFFI CLI (
cargo install weaveffi-cli). -
iOS Rust targets:
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
Step-by-step
1. Author the IDL
Save as greeter.yml:
version: "0.3.0"
modules:
- name: greeter
structs:
- name: Greeting
fields:
- { name: message, type: string }
- { name: lang, type: string }
functions:
- name: hello
params:
- { name: name, type: string }
return: string
- name: greeting
params:
- { name: name, type: string }
- { name: lang, type: string }
return: Greeting
2. Generate bindings
weaveffi generate greeter.yml -o generated --scaffold
You should see, among other targets:
generated/
├── c/
│ └── weaveffi.h
├── swift/
│ ├── Package.swift
│ └── Sources/
│ ├── CWeaveFFI/
│ │ └── module.modulemap
│ └── WeaveFFI/
│ └── WeaveFFI.swift
└── scaffold.rs
3. Implement the Rust library
cargo init --lib mygreeter
mygreeter/Cargo.toml:
[package]
name = "mygreeter"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib", "cdylib"]
[dependencies]
weaveffi-abi = { version = "0.1" }
mygreeter/src/lib.rs:
#![allow(unused)]
#![allow(unsafe_code)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
fn main() {
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use weaveffi_abi::{self as abi, weaveffi_error};
#[no_mangle]
pub extern "C" fn weaveffi_greeter_hello(
name_ptr: *const c_char,
_name_len: usize,
out_err: *mut weaveffi_error,
) -> *const c_char {
abi::error_set_ok(out_err);
let name = unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap_or("world");
let msg = format!("Hello, {name}!");
CString::new(msg).unwrap().into_raw() as *const c_char
}
// Emit the WeaveFFI C ABI runtime symbols (free_string, free_bytes,
// error_clear, cancel_token_*) — one line per cdylib.
abi::export_runtime!();
}
Use scaffold.rs as the template for the rest of the API
(weaveffi_greeter_greeting, the Greeting lifecycle, getters, …).
4. Build for iOS targets
cargo build -p mygreeter --target aarch64-apple-ios --release
cargo build -p mygreeter --target aarch64-apple-ios-sim --release
cargo build -p mygreeter --target x86_64-apple-ios --release
Combine the simulator architectures with lipo and bundle everything
in an XCFramework so Xcode can pick the right slice automatically:
mkdir -p target/universal-ios-sim/release
lipo -create \
target/aarch64-apple-ios-sim/release/libmygreeter.a \
target/x86_64-apple-ios/release/libmygreeter.a \
-output target/universal-ios-sim/release/libmygreeter.a
xcodebuild -create-xcframework \
-library target/aarch64-apple-ios/release/libmygreeter.a \
-headers generated/c/ \
-library target/universal-ios-sim/release/libmygreeter.a \
-headers generated/c/ \
-output MyGreeter.xcframework
5. Wire it into Xcode
- Create a new iOS App in Xcode (SwiftUI or UIKit).
- Drag
MyGreeter.xcframeworkinto the project navigator. Confirm it appears under Build Phases > Link Binary With Libraries. - File > Add Package Dependencies > Add Local… and pick
generated/swift/. The package contributes theCWeaveFFIandWeaveFFItargets. - Build Settings > Header Search Paths: add the path to
generated/c/(e.g.$(SRCROOT)/../generated/c). - Build Settings > Library Search Paths: add the path to the
matching Rust static library
(
$(SRCROOT)/../target/aarch64-apple-ios/releasefor device builds). - Build Phases > Dependencies: ensure
WeaveFFIis listed.
6. Call from Swift
import SwiftUI
import WeaveFFI
struct ContentView: View {
@State private var greeting = ""
var body: some View {
VStack {
Text(greeting)
Button("Greet") {
do {
greeting = try Greeter.hello("Swift")
} catch {
greeting = "Error: \(error)"
}
}
}
.padding()
}
}
The generated WeaveFFI module exposes:
Greeter.hello(_:)— returnsString.Greeter.greeting(_:_:)— returns aGreetinginstance with.messageand.langproperties;deinitcalls the Rust destructor automatically.Greeting— the wrapper class around the opaque Rust pointer.
Verification
-
Select an iOS Simulator target and press Cmd+R.
-
Tap Greet in the running app; the label changes to
Hello, Swift!. -
Re-run on a physical device after building for
aarch64-apple-iosto confirm the device path also works. -
Common error mappings:
Symptom Likely cause Undefined symbols for architecture arm64Static library not linked or the search path is wrong. Module 'CWeaveFFI' not foundHeader search path does not point at generated/c/.No such module 'WeaveFFI'Local Swift package not added under Add Package Dependencies > Add Local…. Crash when running on Intel simulator Build for x86_64-apple-iosand combine withlipo.
Cleanup
rm -rf generated/ MyGreeter.xcframework
cargo clean -p mygreeter
Remove the MyGreeter.xcframework reference from the Xcode project
and undo the Header Search Paths / Library Search Paths
edits.
Next steps
- See the Swift generator reference for the full type mapping.
- Read the Memory Ownership guide to understand
struct lifecycle and
deinitrules. - Try the Calculator tutorial for a simpler end-to-end walkthrough or Android for a JVM target.