Swift-ROS2 3.0: Native Swift ROS 2 Client Library with Generated Common Interfaces and Own Organization
Swift-ros2 3.0: native Swift ROS 2 client library, now with generated common_interfaces and its own org
Swift-ROS2 3.0 is a stable release of a native Swift client library for ROS 2, enabling direct integration with ROS 2 graphs using Zenoh or DDS. It includes generated common interfaces, a new GitHub organization, and support for multiple platforms, with improved compatibility and a migration guide for users upgrading from 2.x.
The release of Swift-ROS2 3.0 introduces a native Swift client library for ROS 2, offering direct integration with ROS 2 graphs via Zenoh or DDS. It includes generated common interfaces and a new GitHub organization, with support for multiple platforms and improved compatibility with upstream ROS 2 versions.
Source: ROS Discourse — Community updates · Read original article ↗
Article text · Original source · English
On this page
swift-ros2 3.0.0 is out. It is the first stable 3.x release of the native Swift client library for ROS 2 that I introduced here in April.
swift-ros2 lets a Swift app join a ROS 2 graph directly, over Zenoh (round-trip compatible with rmw_zenoh_cpp) or DDS (rmw_cyclonedds_cpp). There is no bridge and no rcl cross-compile.

What’s new in 3.0
Three packages, mirroring upstream ROS 2. The project moved to the swift-ros2 GitHub organization:
| Package | What it holds |
|---|---|
| swift-ros2-rosidl 1.0.0 | XCDR v1 codec, message / service / action protocols, runtime interfaces, and the swift-ros2-gen generator with its SwiftPM build plugin |
| swift-ros2 3.0.0 | The runtime: ROS2Context, nodes, publishers, subscriptions, services, actions, parameters |
| swift-ros2-common-interfaces 1.0.0 | std_msgs, std_srvs, geometry_msgs, sensor_msgs, nav_msgs, shape_msgs, stereo_msgs, trajectory_msgs, visualization_msgs, diagnostic_msgs and tf2_msgs, generated from the IDL |
Wire interop
- Services and actions are keyed by their service-level type hashes.
- Over Zenoh a swift-ros2 node announces itself as a real node, with a GID per publisher and client.
- Wire DDS nodes appear in the ROS graph through
ros_discovery_info. - Late-joining action clients get the current status.
Verified against live ROS 2 Humble, Jazzy, Kilted and Lyrical hosts over Zenoh and DDS, through a fresh-user walkthrough of the docs, and in Conduit.
Looking back: the real ROS 2 stack since 2.0
swift-ros2 began in April as a pure-Swift implementation of the ROS 2 wire protocols over zenoh-pico and CycloneDDS. That made it portable, but it meant re-implementing type hashes, QoS rules and graph details and keeping them in step with upstream. The 2.x line changed the foundation:
- 1.3.0 (July) added a native RCL backend: upstream
rclwithrmw_zenoh_cpporrmw_cyclonedds_cpp. It ships as prebuilt xcframeworks on Apple and links a system ROS 2 install on Linux. - 1.4.0 turned it on by default on Apple.
- 2.0.0 (September) removed the wire clients from the public API.
ROS2Contextpicks the backend. The pure-Swift wire path stays as an internal fallback whererclis not available, such as Android, Windows and Humble hosts. - 2.2.0 rebuilt the Apple RCL stack from ROS 2 Lyrical.
On the RCL backend, type hashes, QoS semantics, the node graph and introspection match upstream by construction, because they are upstream. 3.0 builds on that foundation.
A taste of the API
Topics and services
import SwiftROS2
import CommonInterfaces
let context = try await ROS2Context(
transport: .zenoh(locator: "tcp/192.168.1.100:7447"), distro: .jazzy, domainId: 123)
let node = try await context.createNode(name: "sensor_node", namespace: "/ios")
let pub = try await node.createPublisher(Imu.self, topic: "imu")
try pub.publish(Imu(header: Header.now(frameId: "imu_link"),
linearAcceleration: Vector3(x: 0, y: 0, z: 9.81)))
let svc = try await node.createService(TriggerSrv.self, name: "/trigger") { _ in
TriggerSrv.Response(success: true, message: "ok")
}
Actions (example_interfaces/action/Fibonacci, server with feedback, and a client)
actor FibonacciHandler: ActionServerHandler {
typealias Action = FibonacciAction
func handleGoal(_ goal: FibonacciAction.Goal) async -> GoalResponse {
goal.order > 0 ? .accept : .reject
}
func handleCancel(_ handle: ActionGoalHandle<FibonacciAction>) async -> CancelResponse { .accept }
func execute(_ handle: ActionGoalHandle<FibonacciAction>) async throws -> FibonacciAction.Result {
var sequence: [Int32] = [0, 1]
for _ in 2..<10 {
sequence.append(sequence[sequence.count - 1] + sequence[sequence.count - 2])
try await handle.publishFeedback(FibonacciAction.Feedback(sequence: sequence))
}
return FibonacciAction.Result(sequence: sequence)
}
}
let server = try await node.createActionServer(
FibonacciAction.self, name: "/fibonacci", handler: FibonacciHandler())
let client = try await node.createActionClient(FibonacciAction.self, name: "/fibonacci")
try await client.waitForActionServer(timeout: .seconds(5))
let goal = try await client.sendGoal(FibonacciAction.Goal(order: 10))
Task { for await feedback in goal.feedback { print(feedback.sequence) } }
if case .succeeded(let result) = try await goal.result() { print(result.sequence) }
Parameters (visible to ros2 param list / get / set / describe)
_ = try await node.declareParameter(
"rate", default: Int64(30),
descriptor: ROS2ParameterDescriptor(
name: "rate", type: .integer, description: "publish rate in Hz",
integerRange: Int64(1)...Int64(120)))
_ = await node.setOnSetParametersCallback { proposed in
for p in proposed where p.name == "rate" {
if case .integer(let v) = p.value, !(1...120).contains(v) {
return .failure(reason: "rate must be in [1, 120]")
}
}
return .success()
}
Runnable versions of all of these live in Sources/Examples (swift run action-server zenoh, swift run parameter-demo zenoh, …).
Platforms
iOS / iPadOS 16+, macOS 13+, Mac Catalyst, visionOS, Linux (Ubuntu 22.04 / 24.04, x86_64 and aarch64), Windows 10 / 11, and Android API 28+ (Zenoh only).
Upgrading from 2.x? See MIGRATION.md.
See it live
I’m presenting swift-ros2 and Conduit at ROS By-The-Bay on Thursday, October 22, at Intrinsic HQ in Mountain View, with a live demo of Apple devices talking to ROS 2 nodes on Linux:

ROS By-The-Bay October Meetup at Intrinsic: Swift for ROS + ROSCon Global...
ROS By-The-Bay is the Bay Area's only meetup exclusively for Robot Operating System (ROS) users! For our September meetup, we have three fantastic speakers at an amazing ne
Feedback, issues and PRs are very welcome.
Source:ROS Discourse — Community updates · discourse.openrobotics.org