Skip to content
ROS Discourse — Community updates· youtalk·· 4 hours agoSignalEditorial score83

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

Summary

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.

Editorial context

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.

demo

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 rcl with rmw_zenoh_cpp or rmw_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. ROS2Context picks the backend. The pure-Swift wire path stays as an internal fallback where rcl is 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:

Meetup

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

Timezone · UTC

Article dates follow your selected timezone. Briefing editions use Hong Kong time (UTC+8).