A React Native chat application template demonstrating Ankurah integration via UniFFI bindings. This template provides a working example of real-time collaborative data with offline-first capabilities on iOS and Android.
- Real-time sync: WebSocket connection to Ankurah server with live updates
- Local storage: Sled-based persistence with automatic sync
- Reactive UI: Signal-based re-rendering via
useObservehook - Virtual scroll: Efficient pagination for large message lists
- Full chat UI: Rooms, messages, user profiles, inline editing
- Maestro tests: Automated UI test suite included
- macOS (for iOS development)
- Xcode 16.1+
- Node.js 20+
- Ruby with Bundler (for CocoaPods)
- Rust toolchain with iOS targets:
rustup target add aarch64-apple-ios-sim aarch64-apple-ios
# Install JS dependencies
cd react-app
npm install
# Install iOS dependencies
bundle install
bundle exec pod install
cd ..cargo run -p {{project-name}}-serverThe server runs on ws://localhost:9898 and creates a "General" room plus seed data for scroll testing.
./dev.shThis builds the Rust bindings, generates UniFFI TypeScript, and launches the iOS app in the simulator.
For subsequent runs after code changes:
./dev.sh # Incremental rebuild
./dev.sh --clean # Clean rebuild (clears caches)
./dev.sh --hard # Full clean (nukes everything)├── model/ # Shared data models (User, Room, Message)
├── rn-bindings/ # UniFFI bindings exposing Ankurah to React Native
├── server/ # Ankurah WebSocket server
├── react-app/ # React Native application
│ ├── App.tsx # Main app entry point
│ ├── src/
│ │ ├── components/ # UI components (Chat, RoomList, etc.)
│ │ ├── hooks/ # React hooks (useObserve, useSignal, etc.)
│ │ └── generated/ # Auto-generated UniFFI TypeScript bindings
│ └── ios/ # iOS native project
├── maestro/ # Automated UI tests
└── dev.sh # Build and run script
-
Define your model in
model/src/lib.rs:#[derive(Model, Debug, Serialize, Deserialize)] pub struct MyModel { pub field: String, }
-
Rebuild bindings:
./dev.sh --clean -
Use in React Native:
const items = useObserve(() => liveQuery.items());
Edit rn-bindings/src/lib.rs:
const DEFAULT_SERVER_URL: &str = "ws://your-server:9898";Or pass a custom URL when initializing in App.tsx.
# Run Maestro UI tests (requires running app)
./run-maestro-tests.sh
# Run Rust tests
cargo testBuild fails with UniFFI errors: Run ./dev.sh --hard to clean all caches.
App can't connect to server: Ensure server is running and check firewall settings. iOS simulator uses localhost directly.
Rust logs: Appear in the Metro console prefixed with [Rust/INFO], [Rust/WARN], etc. Use the in-app debug panel (tap Debug button) for scroll state info.
MIT OR Apache-2.0