Skip to content

Built a native macOS messaging client in Swift 6 and SwiftUI — 11,141 lines across 53 files — over the C interface of a Rust core linked as a static archive from a pinned revision.

Situation. The messaging protocol had a well‑tested core written in Rust and clients on several platforms, but the desktop experience on macOS was a cross‑platform shell — it did not look like a Mac application, did not behave like one, and carried a runtime that a native application does not need. The core exposes its capability through a C interface, which means any language that can call C can build on it, and Swift can.

Task. A native client had to be built directly on that C interface, in the platform’s current language and current interface framework, with the core linked in rather than shipped alongside.

Action. The application is 11,141 lines of Swift across fifty‑three files in the app target, built on SwiftUI with the core linked as a static archive compiled from a pinned upstream revision. Pinning the revision rather than tracking a branch is the decision that makes the build reproducible: the C interface is the contract, and a moving core changes the contract without changing a line of Swift. The layering keeps the unsafe surface small — a thin wrapper owns every pointer and every string that crosses the boundary, models above it are ordinary Swift values, and the interface layer never sees a raw pointer. Everything the C interface returns has an ownership rule, and getting one wrong produces either a leak or a crash with no compiler warning in between, so the wrapper is where the entire project’s memory discipline lives. The build is driven by a task runner with fifty‑six targets covering the core compile, the Swift build, the test suites, the linters and the packaging.

Result. A native client that starts as a Mac application, uses the platform’s own materials and controls, and carries no embedded browser. What has not been done should be stated: this is not a shipped release. There is no notarised distribution, no update channel, and one language in the interface, so it is a working application rather than a product anyone else is running.