SignalR Swift is a client library for connecting to SignalR servers from Swift applications.
- Swift >= 5.10
- macOS >= 11.0
- iOS >= 14
Add the project as a dependency to your Package.swift from a verion:
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "signalr-client-app",
dependencies: [
.package(url: "https://github.com/dotnet/signalr-client-swift", .upToNextMinor(from: "1.0.0-preview.3"))
],
targets: [
.executableTarget(name: "YourTargetName", dependencies: [.product(name: "SignalRClient", package: "signalr-client-swift")])
]
)
Also you can add the project as a dependency to your Package.swift from a git branch:
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "signalr-client-app",
dependencies: [
.package(url: "https://github.com/dotnet/signalr-client-swift", branch: "main")
],
targets: [
.executableTarget(name: "YourTargetName", dependencies: [.product(name: "SignalRClient", package: "signalr-client-swift")])
]
)
The you can use with import SignalRClient
in your code.
To entablish a connection, create a HubConnectionBuilder
and call build()
. Url is required to connect to a server, thus you need to use withUrl()
while building the connection. After the connection is built, you can call start()
to connect to the server.
import SignalRClient
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server")
.build()
try await connection.start()
You can listen for events from the server using the on
method. The on
method takes the name of the method and a closure that will be called when the server calls the method.
await connection.on("ReceiveMessage") { (user: String, message: String) in
print("\(user) says: \(message)")
}
Swift clients can also call public methods on hubs via the send method of the HubConnection. invoke
will wait until the server response. And it will throw if there's an error sending message. Unlike the invoke
method, the send
method doesn't wait for a response from the server. Consequently, it's not possible to return data or errors from the server.
try await connection.invoke(method: "SendMessage", arguments: "myUser", "Hello")
try await connection.send(method: "SendMessage", arguments: "myUser", "Hello")
In addition to making calls to clients, the server can request a result from a client. This requires the server to use ISingleClientProxy.InvokeAsync
and the client to return a result from its .on
handler.
await connection.on("ClientResult") { (message: String) in
return "client response"
}
In the following example, the server calls the ClientResult
method on the client and waits for the client to return a result. The message will be "client response".
public class ChatHub : Hub
{
public async Task TriggerClientResult()
{
var message = await Clients.Client(connectionId).InvokeAsync<string>("ClientResult");
}
}
To receive a stream of data from the server, use the stream
method:
let stream: any StreamResult<String> = try await connection.stream(method: "StreamMethod")
for try await item in stream.stream {
print("Received item: \(item)")
}
To send a stream of data from the client to the server, use the AsyncStream
:
let (clientStream, continuation) = AsyncStream.makeStream(of: Int.self)
try await connection.send("UploadStream", arguments: clientStream)
for i in 1...100 {
continuation.yield(i)
}
continuation.finish()
The swift client for SiganlR supports automatic reconnect. You can enable it by calling withAutomaticReconnect()
while building the connection. It won't automatically reconnect by default.
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server")
.withAutomaticReconnect()
.build()
Without any parameters, WithAutomaticReconnect
configures the client to wait 0, 2, 10, and 30 seconds respectively before trying each reconnect attempt. After four failed attempts, it stops trying to reconnect.
Before starting any reconnect attempts, the HubConnection
transitions to the Reconnecting
state and fires its onReconnecting
callbacks.
After the reconnection succeeds, the HubConnection
transitions to the connected
state and fires its onReconnected
callbacks.
A general way to use onReconnecting
and onReconnected
is to mark the connection state changes:
connection.onReconnecting { error in
// connection is disconnected because of error
}
connection.onReconnected {
// connection is connected back
}
In order to configure a custom number of reconnect attempts before disconnecting or change the reconnect timing, withAutomaticReconnect
accepts an array of numbers representing the delay in seconds to wait before starting each reconnect attempt.
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server")
.withAutomaticReconnect([0, 0, 1]) // wait 0, 0, and 1 second before trying to reconnect and stop after 3 attempts
.build()
For more control over the timing and number of automatic reconnect attempts, withAutomaticReconnect accepts an object implementing the RetryPolicy
protocol, which has a single method named nextRetryInterval
. The nextRetryInterval
takes a single argument with the type RetryContext
. The RetryContext has three properties: retryCount
, elapsed
and retryReason
which are a Int, a TimeInterval and an Error respectively. Before the first reconnect attempt, both retryCount
and elapsed
will be zero, and the retryReason
will be the Error that caused the connection to be lost. After each failed retry attempt, retryCount
will be incremented by one, elapsed
will be updated to reflect the amount of time spent reconnecting so far in seconds, and the retryReason
will be the Error that caused the last reconnect attempt to fail.
// Define a customized retry policy
struct CustomRetryPolicy: RetryPolicy {
func nextRetryInterval(retryContext: RetryContext) -> TimeInterval? {
return 1 // unlimited retry with 1 second
}
}
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server")
.withAutomaticReconnect(CustomRetryPolicy())
.build()
Options | Default Value | Description |
---|---|---|
withKeepAliveInterval | 15 (seconds) | Determines the interval at which the client sends ping messages and is set directly on HubConnectionBuilder. This setting allows the server to detect hard disconnects, such as when a client unplugs their computer from the network. Sending any message from the client resets the timer to the start of the interval. If the client hasn't sent a message in the ClientTimeoutInterval set on the server, the server considers the client disconnected. |
withServerTimeout | 30 (seconds) | Determines the interval at which the client waits for a response from the server before it considers the server disconnected. This setting is set directly on HubConnectionBuilder. |
The SignalR Swift client supports three transports: LongPolling, ServerSentEvents, and WebSockets. By default, the client will use WebSockets if the server supports it, and fall back to ServerSentEvents and LongPolling if it doesn't. You can configure the client to use a specific transport by calling withUrl(url:transport:)
while building the connection.
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server", transport: .webSockets) // use websocket only
.build()
let connection = HubConnectionBuilder()
.withUrl(url: "https://your-signalr-server", transport: [.webSockets, .serverSentEvents]) // use websockets and server sent events
.build()
Feature | Supported |
---|---|
Azure SignalR Service Support | ✅ |
Automatic Reconnection | ✅ |
Stateful Reconnect | |
Server to Client Streaming | ✅ |
Client to Server Streaming | |
Long Polling | ✅ |
Server-Sent Events | ✅ |
WebSockets | ✅ |
JSON Protocol | ✅ |
MessagePack Protocol | ✅ |
Client Results | ✅ |
.NET (including the runtime repo) is licensed under the MIT license.