Ivan FB 014e1618ba
docs(examples): add iOS (Swift) example over the native C ABI
A SwiftPM package wrapping the timer library's native ABI behind an idiomatic
`TimerNode` Swift class. `build-xcframework.sh` cross-compiles the Nim library
to a static MyTimer.xcframework with three slices — ios-arm64 (device),
ios-arm64-simulator, and macos-arm64 — assembling the .xcframework by hand so it
works without a functioning Simulator toolchain (CI-friendly).

The wrapper bridges the async FFI-thread callback to a synchronous Swift API
with a semaphore and reads the typed EchoResponse struct out of the callback.
The macos-arm64 slice makes the wrapper testable on the host: `swift test`
passes against it. Device/simulator slices are the real iOS deployment artifacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:37:19 +02:00

2.5 KiB

iOS example — Swift over the native C ABI

A SwiftPM package that wraps the timer library's native (zero-serialization) C ABI behind an idiomatic Swift class, TimerNode. The library is cross-compiled to a static .xcframework and consumed from Swift; struct returns come back as typed Swift values.

let node = try TimerNode(name: "my-app")
print(try node.version())                 // "nim-timer v0.1.0"
let r = try node.echo("hello", delayMs: 5) // EchoResult
print(r.echoed, r.timerName)

Layout

Path Description
build-xcframework.sh Cross-compiles the Nim lib to MyTimer.xcframework (ios-arm64 device, ios-arm64 simulator, macos-arm64) and bundles the C headers + module map.
cheaders/ The native C header (my_timer.h), CBOR header, and module.modulemap (module CMyTimer).
Sources/MyTimer/MyTimer.swift The Swift wrapper. Bridges the async FFI-thread callback to a synchronous Swift API with a semaphore; reads the typed EchoResponse struct out of the callback.
Package.swift SwiftPM: a binary target (the xcframework) + the Swift wrapper + tests.
Tests/ Unit tests, runnable on the host via the macOS slice.

Build & test

cd examples/timer/ios
./build-xcframework.sh     # builds the 3 slices into MyTimer.xcframework
swift test                 # runs on the host (macos-arm64 slice)

build-xcframework.sh assembles the .xcframework directly (no xcodebuild -create-xcframework), so it works in headless / CI environments. The macos-arm64 slice exists only so the wrapper is testable with swift test; real iOS deployment uses the ios-arm64 (device) and ios-arm64-simulator slices.

Use it in an iOS app

  1. Run ./build-xcframework.sh.
  2. Add this directory as a local Swift package (Xcode → Add Package Dependencies… → Add Local…), or depend on it in your own Package.swift.
  3. import MyTimer and use TimerNode.

Notes

  • This is the native, same-process path — the app links the library directly. The CBOR ABI is for inter-process communication only (see ../ipc).
  • Each call is dispatched on the library's background FFI thread; TimerNode blocks on a semaphore until the result callback fires. A struct return (e.g. EchoResponse) is read inside the callback — it is valid only for the callback's lifetime — and copied into the returned Swift value.
  • Regenerate cheaders/my_timer.h from the repo root with nimble genbindings_c if the library's API changes.