A Swift implementation of peertalk - USB communication between iOS and macOS.
PeerTalkSwift enables direct communication between iOS devices and macOS over USB through the usbmuxd daemon. It provides a modern Swift API with full async/await support and Swift 6 concurrency.
- iOS 15.0+ / macOS 12.0+
- Swift 6.0+
- Xcode 16.0+
dependencies: [
.package(url: "https://github.com/user/PeerTalkSwift.git", from: "1.0.0")
]// Project.swift
dependencies: [
.external(name: "PeerTalkSwift")
]import PeerTalkSwift
// Listen for device attach/detach events
for try await event in USBHub.shared.startListening() {
switch event {
case .deviceAttached(let device):
print("Device connected: \(device.serialNumber)")
case .deviceDetached(let deviceID):
print("Device disconnected: \(deviceID)")
}
}import PeerTalkSwift
class MyDelegate: ChannelDelegate {
func channel(_ channel: Channel, didReceiveFrame frame: Frame) {
print("Received frame type: \(frame.type)")
if let payload = frame.payload {
print("Payload: \(payload)")
}
}
func channel(_ channel: Channel, didEndWithError error: Error?) {
print("Channel closed: \(error?.localizedDescription ?? "no error")")
}
}
let channel = Channel()
channel.delegate = MyDelegate()
// Connect to device on port 2345
try await channel.connect(toDevice: device, port: 2345)
// Send data
let data = "Hello from Mac!".data(using: .utf8)!
try await channel.send(type: 100, payload: data)import PeerTalkSwift
class ServerDelegate: ChannelServerDelegate {
func server(_ server: ChannelServer, didAcceptChannel channel: Channel) {
print("New connection accepted")
channel.delegate = self
}
}
let server = ChannelServer()
server.delegate = ServerDelegate()
try server.listen(onPort: 2345)PeerTalkSwift/
├── USBMux/ # usbmuxd protocol layer
│ ├── USBMuxPacket # Packet encoding/decoding (plist)
│ └── USBMuxConnection # Unix socket communication
├── USBHub/ # Device management
│ ├── USBDevice # Device model
│ └── USBHub # Device listening (actor)
├── Protocol/ # Frame protocol
│ ├── Frame # Frame structure
│ └── FrameProtocol # Frame encoding/decoding
└── Channel/ # High-level API
├── Channel # Client channel
└── ChannelServer # Server listener
-
USBMux Layer: Communicates with macOS
usbmuxddaemon via Unix socket at/var/run/usbmuxd. Uses plist-encoded packets. -
Frame Layer: Application-level protocol with 16-byte header:
| version (4) | type (4) | tag (4) | payload_size (4) | payload (N) | -
Channel Layer: High-level abstraction with delegate pattern for sending/receiving frames.
public actor USBHub {
static let shared: USBHub
var connectedDevices: [USBDevice]
func startListening() -> AsyncThrowingStream<USBHubEvent, Error>
func stopListening()
func connect(to device: USBDevice, port: UInt16) async throws -> Int32
}public final class Channel {
var delegate: ChannelDelegate?
var isConnected: Bool
var userInfo: Any?
func connect(toDevice: USBDevice, port: UInt16) async throws
func send(type: UInt32, tag: UInt32, payload: Data?) async throws
func sendWithNewTag(type: UInt32, payload: Data?) async throws -> UInt32
func close()
func cancel()
}public struct Frame: Sendable {
static let noTag: UInt32 = 0
static let endOfStreamType: UInt32 = 0
let type: UInt32
let tag: UInt32
let payload: Data?
}MIT