Monthly Archives: June 2020

Automatic Reconnection in the Swift SignalR Client

As of version 0.7.0 the Swift SignalR Client supports automatic reconnection. This means that, if configured, the client will try to re-establish the connection to the server if the connection was lost. This post explains how this feature works, how to enable it and what configuration options are available.

Automatic reconnects

There are many scenarios where restoring an interrupted connection automatically is important. For instance, mobile applications very often have to be able to deal with unstable network and it’s crucial for these apps to be resilient to network issues. Conceptually the solution to the problem is simple – when the connection is lost a new connection needs to be started. In case of the Swift SignalR Client, this could always be implemented by the code consuming the client (i.e. on the application side). It turns out however, that in practice, implementing this logic is quite hard. Given that this is a common request and is the implementation is tricky it made sense to add support for automatic reconnection to the client. An important thing to note however is that the client does not offer anything more than just restoring the connection. In other words, the client will make a few attempts to restart the connection if it was stopped due to an error but will not do anything more than that. If reconnecting succeeds the server will treat the connection as a completely new connection and will assign it a new connection id. The client will also not receive messages it might have missed when it was disconnected. Anyone who used the non-Core version of SignalR can notice that this a big change to how reconnection worked in the non-Core version where, upon reconnecting, the server would recognize that the client has reconnected and resend missed messages. This functionality can no longer be implemented in the client for the Core version of SignalR because it requires cooperation from the server side (e.g. the server needs to buffer messages) and that logic does not exist in the Core version of SignalR.

Automatic reconnection is disabled by default. The main reason for this is backward compatibility – existing applications did not expect the connection to try to reconnect automatically so, they could break if the feature was enabled by default. Automatic reconnection requires also a bit of additional work and handle new lifecycle events.

Basics

The easiest way to enable automatic reconnection is to use the new .withAutoReconnect() method available on the HubConnectionBuilder class.

When automatic reconnection is enabled the application may receive two additional events:

  • connectionWillReconnect – invoked when the connection was lost
  • connectionDidReconnect – invoked when the connection was successfully restored

By default, the client will make up to four attempts to restore the connection. The first attempt will be made immediately after the connection was lost. The next three attempts will take place respectively 2, 10 and 30 seconds after the previous unsuccessful attempt. If all four attempts are unsuccessful the client will give up, close the connection and invoke the connectionDidClose event.

When the connection is being restored the client will not allow to invoke any method that tries to send data to the server.

Connection is now restartable

Adding support for automatic reconnection made the connection restartable. Before, once the connection was stopped it was necessary to create a new instance to be able to connect to the server again. This is no longer the case – the same instance can be used to restart connection to the server after it was stopped. This could be especially useful when handling background/foreground transitions.

Advanced Scenarios

The default reconnect configuration can be customized. It is possible to change the number of attempts as well as the time intervals between the attempts. The easiest way to do this is to create a new instance of the DefaultReconnectPolicy class with an array of retry intervals and pass this policy to the .withAutoReconnect() method. The number of retry intervals in the array tells the client how many reconnect attempts it should make, while the interval values indicate the time to wait between the attempts.

If the default reconnect policy is not flexible enough it is possible to go even further and create a custom reconnect policy by creating a class that conforms to the ReconnectPolicy protocol. This protocol has just one method – nextAttemptInterval that takes a RetryContext and returns the time interval telling the client when the next reconnect attempt should happen. The RetryContext instance passed to the nextAttempInterval method contains information about the current reconnect – the time when the reconnect was initiated, the number of failed attempts so far and the original error that triggered the reconnect. To stop further reconnect attempts the nextAttempInterval method should return DispatchTimeInterval.never. To put the policy to work the policy needs to be passed to the .withAutoReconnect() method when configuring the connection.

Backward Compatibility (a.k.a. Kill Switch)

As noted above, writing reconnect logic turned out to be quite tricky. It also required modifying existing code that is executed even when automatic reconnects are disabled. This created a risk of introducing issues into existing scenarios. In case of running into a bug like this it is possible to go back to the previous behavior by using the .withLegacyHttpConnection() method on the HubConnectionBuilder when creating a new hub connection.

Conclusion

These are pretty much all the details needed to be able to use automatic reconnection in the Swift SignalR Client. My hope is that automatic reconnection will make lives of developers much easier.