Swift Libraries
The Swift bindings expose Media over QUIC to iOS, iPadOS, macOS, and the iOS Simulator. Built on the same Rust core (moq-ffi) as the Python and Kotlin packages, wrapped with an idiomatic async/await API.
Packages
Two Swift packages, split so the ergonomic API can evolve on its own cadence:
Moq
The package you want. A Swift-native wrapper: de-prefixed types (Client, Session, BroadcastProducer), AsyncSequence conformance on every consumer, structured-concurrency cancellation, and Sendable handles. The raw MoqFFI types stay behind it.
Features:
- iOS 15+, iPadOS 15+, macOS 12.3+
- Apple Silicon binary for macOS and the iOS Simulator
- iOS device + iOS Simulator slices (arm64)
- Cancellation through Swift
Taskpropagates to native consumers - Versioned independently of the Rust crates; floats to the latest compatible
MoqFFIpatch
MoqFFI
The raw UniFFI bindings (the Moq-prefixed classes) plus the prebuilt MoqFFI.xcframework, tracking the moq-ffi Rust crate one-to-one. Moq pulls this in for you; depend on it directly only if you need the unwrapped API.
Installation
Add the Moq wrapper; SPM resolves MoqFFI (and its XCFramework) transitively:
dependencies: [
.package(url: "https://github.com/moq-dev/moq-swift", from: "0.4.4"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "Moq", package: "moq-swift"),
],
),
]Or in Xcode: File → Add Package Dependencies → enter the URL.
The transitive MoqFFI.xcframework is attached to the matching moq-ffi-v* release on the source repo. SPM downloads it transparently; no manual asset handling required.
Quickstart
import Moq
let client = Client()
let session = try await client.connect(to: "https://relay.example.com")
// session.publisher and session.consumer are always populated: by whatever
// origin you wired via setPublish / setConsume before connect, or by a fresh
// auto-created one. The duplex no-config path (the typical client) shares one
// origin between both sides.
let announced = try session.consumer.announced(prefix: "demos/")
for try await announcement in announced {
print("got broadcast \(announcement.path)")
let catalog = try announcement.broadcast.subscribeCatalog()
for try await update in catalog {
print("catalog: \(update)")
}
}
session.shutdown()Cancelling the surrounding Swift Task propagates through to the underlying cancel() calls on each consumer.
Source and issues
- API reference: Swift Package Index (DocC)
- Source: swift/ (in the monorepo)
- Mirrors (what SPM resolves): moq-dev/moq-swift (wrapper), moq-dev/moq-swift-ffi (raw bindings)
- README: swift/README.md