Skip to content

AkhilaRM/SwiftR

 
 

Repository files navigation

SwiftR

Join the chat at https://gitter.im/adamhartford/SwiftR

A Swift client for SignalR. Supports hubs and persistent connections.

Demo

I have published a sample SignalR server at http://swiftr.azurewebsites.net. The iOS demo application now uses this server. See SwiftRChat for the souce code. It's based on this, with some minor changes:

http://www.asp.net/signalr/overview/deployment/using-signalr-with-azure-web-sites

How does it work?

It's a wrapper around the SignalR JavaScript client running in a hidden web view. As such, it's subject to the same limitations of that client -- namely, no support for custom headers when using WebSockets. This is because the browser's WebSocket client does not support custom headers.

// Client
let connection = SignalR("https://swiftr.azurewebsites.net")
connection.useWKWebView = true

Also when using WKWebView, make sure to enable CORS on your server:

// Server
app.UseCors (CorsOptions.AllowAll);

// See my SignalRApplication repo for a CORS example with ASP.NET Core.

How to set Origin

If allowing all origins (*) is not acceptable, you can specify an allowed origin via the originUrlString property.

connection.originUrlString = "http://www.example.com"

What versions of SignalR are supported?

SwiftR supports SignalR version 2.x. Version 2.2.2 is assumed by default. To change the SignalR version:

let connection = SignalR("https://swiftr.azurewebsites.net")
connection.signalRVersion = .v2_2_2
//connection.signalRVersion = .v2_2_1
//connection.signalRVersion = .v2_2_0
//connection.signalRVersion = .v2_1_2
//connection.signalRVersion = .v2_1_1
//connection.signalRVersion = .v2_1_0
//connection.signalRVersion = .v2_0_3
//connection.signalRVersion = .v2_0_2
//connection.signalRVersion = .v2_0_1
//connection.signalRVersion = .v2_0_0

Installation

CocoaPods:

use_frameworks!
pod 'SwiftR'

Carthage:

github 'adamhartford/SwiftR'

Server Example

See https://github.com/adamhartford/SignalRDemo for a sample self-hosted SignalR application. Or, https://github.com/adamhartford/SignalRApplication for an ASP.NET 5 version.

Simple Example (Hub)

// Server
public class SimpleHub : Hub 
{
    public void SendSimple(string message, string detail)
    {
        Clients.All.notifySimple (message, detail);
    }
}

Default parameter names in callback response:

// Client
let connection = SignalR("http://localhost:5000")

let simpleHub = Hub("simpleHub")
simpleHub.on("notifySimple") { args in
    let message = args![0] as! String
    let detail = args![1] as! String
    print("Message: \(message)\nDetail: \(detail)")
}

connection.addHub(simpleHub)
connection.start()

...

// Invoke server method
simpleHub.invoke("sendSimple", arguments: ["Simple Test", "This is a simple message"])

// Invoke server method and handle response
simpleHub.invoke("sendSimple", arguments: ["Simple Test", "This is a simple message"]) { (result, error) in
    if let e = error {
        print("Error: \(e)")
    } else {
        print("Success!")
        if let r = result {
            print("Result: \(r)")
        }
    }
}

Complex Example (Hub)

// Server
public class ComplexMessage
{
    public int MessageId { get; set; }
    public string Message { get; set; }
    public string Detail { get; set; }
    public IEnumerable<String> Items { get; set; }
}

// Server
public class ComplexHub : Hub
{
    public void SendComplex(ComplexMessage message) 
    {
        Clients.All.notifyComplex (message);
    }
}
// Client
let connection = SignalR("http://localhost:5000")

let complexHub = Hub("complexHub")
complexHub.on("notifyComplex") { args in
    let m: AnyObject = args![0] as AnyObject!
    print(m)
}

connection.addHub(complexHub)
connection.start()

...

let message = [
    "messageId": 1,
    "message": "Complex Test",
    "detail": "This is a complex message",
    "items": ["foo", "bar", "baz"]
]

// Invoke server method
complexHub.invoke("sendComplex", parameters: [message])

Persistent Connections

// Server
app.MapSignalR<MyConnection> ("/echo");

...

public class MyConnection : PersistentConnection 
{
    protected override Task OnReceived(IRequest request, string connectionId, string data) 
    {
        return Connection.Broadcast(data);
    }
}
// Client
let persistentConnection = SignalR("http://localhost:8080/echo", connectionType: .persistent)
persistentConnection.received = { data in
    print(data)
}
persistentConnection.start()

// Send data
persistentConnection.send("Persistent Connection Test")

Transport Method

By default, SignalR will choose the best transport available to you. You can also specify the transport method:

let connection = SignalR("https://swiftr.azurewebsites.net")
connection.transport = .auto // This is the default
connection.transport = .webSockets
connection.transport = .serverSentEvents
connection.transport = .foreverFrame
connection.transport = .longPolling

Connection Lifetime Events

SwiftR exposes the following SignalR events:

let connection = SignalR("http://swiftr.azurewebsites.net")
connection.started = { print("started") }
connection.connected = { print("connected: \(connection.connectionID)") }
connection.connectionSlow = { print("connectionSlow") }
connection.reconnecting = { print("reconnecting") }
connection.reconnected = { print("reconnected") }
connection.disconnected = { print("disconnected") }
connection.start()

Reconnecting

You may find it necessary to try reconnecting manually once disconnected. Here's an example of how to do that:

connection.disconnected = {
    print("Disconnected...")
    
    // Try again after 5 seconds
    let delayTime = DispatchTime.now() + .seconds(5)
    DispatchQueue.main.asyncAfter(deadline: delayTime) { [weak self] in
        connection.start()
    }
}

Stop/Start Connection

Use the stop() and start() methods to manage connections manually.

let connection  = SignalR("https://swiftr.azurewebsites.net")
connection.start()
connection.stop()

...

if connection.state == .connected {
    connection.stop()
} else if connection.state == .disonnected {
    connection.start()
}

Connection State

public enum State {
    case connecting
    case connected
    case disconnected
}

Sending information to SignalR

Query String

let connection = SignalR("https://swiftr.azurewebsites.net")
connection.queryString = ["foo": "bar"]

Custom Headers (Non-WebSocket Only)

let connection = SignalR("https://swiftr.azurewebsites.net")
connection.headers = ["X-MyHeader1": "Value1", "X-MyHeader2", "Value2"]

SwiftR will send any cookies in your app's NSHTTPCookieStorage to SignalR. You can also set cookies manually:

let cookieProperties = [
    NSHTTPCookieName: "Foo",
    NSHTTPCookieValue: "Bar",
    NSHTTPCookieDomain: "myserver.com",
    NSHTTPCookiePath: "/",
]
let cookie = NSHTTPCookie(properties: cookieProperties)
NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie!)

Error Handling

connection.error = { error in 
  print("Error: \(error)")
  
  if let source = error?["source"] as? String, source == "TimeoutException" {
      print("Connection timed out. Restarting...")
      connection.start()
  }
}

License

SwiftR is released under the MIT license. See LICENSE for details.

About

Swift client for SignalR (iOS and Mac)

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Swift 90.6%
  • JavaScript 5.7%
  • Objective-C 2.3%
  • Ruby 1.4%