Typed Proxies & Server Methods
The Swift client supports compile-time proxy generation via the @HubProxy macro and server-to-client method handling.
@HubProxy Macro
Mark a protocol with @HubProxy to generate a typed proxy class at build time:
import CocoarSignalARRRMacros
@HubProxy
protocol IChatHub {
func sendMessage(user: String, message: String) async throws
func getHistory() async throws -> [String]
func streamMessages() async throws -> AsyncThrowingStream<String, Error>
}The macro generates IChatHubProxy that routes each method to the correct connection call:
| Return type | Generated call | Protocol |
|---|---|---|
async throws (void) | connection.send("IChatHub|method", ...) | SendMessage |
async throws -> T | connection.invoke("IChatHub|method", ...) | InvokeMessageResult |
async throws -> AsyncThrowingStream<T, Error> | connection.stream("IChatHub|method", ...) | StreamMessage |
Use typed proxies
let chat = connection.getTypedMethods(IChatHubProxy.self)
try await chat.sendMessage(user: "Alice", message: "Hello!")
let history = try await chat.getHistory()
for try await msg in try await chat.streamMessages() {
print(msg)
}Server-to-client handlers
Register handlers for methods the server can call on the client:
await connection.onServerMethod("ReceiveMessage") { args in
let user = args[0] as! String
let message = args[1] as! String
print("\(user): \(message)")
return AnyCodable(nilLiteral: ())
}
await connection.onServerMethod("GetClientName") { _ in
return AnyCodable(stringLiteral: UIDevice.current.name)
}Handlers return AnyCodable — a type-erased wrapper that supports all JSON-serializable types.
Streaming handlers
For server-to-client streaming methods, use onServerStreamMethod:
await connection.onServerStreamMethod("StreamData") { args in
AsyncThrowingStream { continuation in
for i in 0..<10 {
continuation.yield(AnyCodable(integerLiteral: i))
}
continuation.finish()
}
}Interface registration
For structured handler registration with a prefix:
await connection.registerHandlers(prefix: "ChatClient", handlers: [
"ReceiveMessage": { args in
// handle message
return AnyCodable(nilLiteral: ())
},
"GetClientName": { _ in
return AnyCodable(stringLiteral: "SwiftClient")
},
])Or implement the ServerInterfaceHandler protocol:
struct MyChatClient: ServerInterfaceHandler {
static var interfaceName: String { "ChatClient" }
func handlers() -> [String: @Sendable ([Any]) async throws -> AnyCodable] {
[
"ReceiveMessage": { args in /* ... */ },
"GetClientName": { _ in AnyCodable(stringLiteral: "SwiftClient") },
]
}
}
await connection.registerInterface(MyChatClient())Cancellation support
When the server passes a CancellationToken to a client method, the Swift client uses an actor-based CancellationManager. The handler's continuation is cancelled when the server sends CancelTokenFromServer.
HTTP stream references
The Swift client supports StreamReference resolution. When a server-to-client call includes a Stream parameter, the client detects the StreamReference marker and downloads the data via HTTP:
let data = try await StreamReferenceResolver.resolve(streamRef)INFO
Stream reference detection in server method dispatch is available. The client downloads via URLSession.
Next steps
- Setup & Usage — connection basics
- Cancellation Propagation — how cancellation works across clients
- HTTP Stream References — large file transfer