ConnectivityBridge is a Swift Package that provides a typed request/response bridge on top of WatchConnectivity. It ships with a fully working iOS + watchOS demo app so you can run the code on simulators or real devices and see the communication flow end-to-end.
Read the full documentation in ConnectivityBridge/DOCS.md.
Add ConnectivityBridge to your Package.swift:
dependencies:[.package(url:"https://github.com/davidthorn/ConnectivityBridge.git", from:"0.1.0")],targets:[.target(
name:"YourApp",
dependencies:[.product(name:"ConnectivityBridge",package:"ConnectivityBridge")])]This project is both a library and a teaching tool. It shows:
- How to package WatchConnectivity infrastructure as a reusable Swift Package.
- A clear, visual request → reply flow on both phone and watch.
- A typed request/response bridge (
TypedConnectivityBridge) with timeouts. - A simpler transport bridge (
WatchConnectivityBridge) for raw WatchConnectivity plumbing. - How to pair iOS and watchOS simulators and test the bridge end-to-end.
- iOS/watchOS developers who want a Swift Package for WatchConnectivity messaging.
- People recording tutorials or onboarding new team members.
- Anyone who wants a clean base for WatchConnectivity experiments.
- ConnectivityBridge (Swift Package): The reusable bridge you can import into any iOS/watchOS app.
- Demo App (iOS + watchOS): A fully working example that visualizes request/reply flow, status, and latency.
The demo lives alongside the package so you can learn the API and see it working in real time.
iPhone
Watch
Minimal setup that mirrors how the demo uses the bridge:
letbridge=TypedConnectivityBridge<BridgeMessage,BridgeMessage>(
transport:WatchConnectivityBridge.shared
)
bridge.connect()Task{letevents=await bridge.events()forawaiteventin events {print(event)}}Task{letrequests=await bridge.requests()forawaitrequestin requests {letreply=BridgeMessage(text:"Pong", origin:"Watch", id: request.id)await bridge.reply(to: request.id, with: reply)}}TypedConnectivityBridge<Request, Response> handles typed request/response flows with correlation IDs and timeouts. It wraps payloads internally so replies can be matched to requests safely.
Use send(_:) when you do not expect a response.
letbridge=TypedConnectivityBridge<BridgeMessage,BridgeMessage>(
transport:WatchConnectivityBridge.shared
)letmessage=BridgeMessage(text:"One-way ping", origin:"iPhone")tryawait bridge.send(message)Use request(_:timeout:) when you need a reply. If the reply doesn’t arrive before the timeout, the call throws.
letbridge=TypedConnectivityBridge<BridgeMessage,BridgeMessage>(
transport:WatchConnectivityBridge.shared
)letrequest=BridgeMessage(text:"Ping?", origin:"iPhone")letreply=tryawait bridge.request(request, timeout:.seconds(2))print("Reply: \(reply.text)")Listen for incoming requests and reply explicitly. The bridge will match the response to the original request.
Task{letrequests=await bridge.requests()forawaitreqin requests {letreply=BridgeMessage(text:"Pong", origin:"Watch", id: req.id)await bridge.reply(to: req.id, with: reply)}}WatchConnectivityBridge is the low-level transport layer. It exposes a typed stream of ConnectionSnapshot<T> so you can observe state and payloads.
lettransport=WatchConnectivityBridge.shared
transport.connect()Task{letstream=await transport.stream(as:BridgeMessage.self)forawaitsnapshotin stream {iflet received = snapshot.received {print("Received: \(received.text)")}}}lettransport=WatchConnectivityBridge.shared
letpayload=BridgeMessage(text:"Hello", origin:"iPhone")tryawait transport.send(payload)- Open the demo app project and run both the iOS app and the watchOS app.
- Use Xcode → Device and Simulators to ensure the watch simulator is paired with your phone simulator.
- When paired, the UI shows Connected / Reachable and requests begin to succeed.
- The UI is intentionally verbose so it can be shown in tutorials without narration.
- The “Latest Activity” section is a full log with timestamps, direction, and request IDs.
- For full API and usage documentation, see
ConnectivityBridge/DOCS.md.

