State of Swift WebSockets
Apple delivered several exciting and innovative new APIs for developers at WWDC this year. You might have heard or read about the SwiftUI and Combine revamp, but I want to address the new world of native WebSockets! The historically complicated connection protocol is now delivered as a first class citizen! Before we dive in on how awesome this is, it's important to have a basic understanding of Sockets and the road we've traveled to reach this point.
What is a socket?
By definition a socket is one end of a two-way communication link between two programs running on a network. When a program runs on a network it is assigned an ip address and a port number (ie. 10.0.0.1:80). Using this data we can identify and locate the application we want to communicate with. Let's break it down with an example!
When we connect to a website like "www.google.com" through a web browser we have two programs running. Those two programs are Google's web server application and the web browser application installed on our machine. There is an ip address and port number mapped to the web address behind "www.google.com" and a unique address for our web browser. Our computers have two sockets created to be able to transport information between each other.
A multitude of different protocols have been created that allow us to communicate in different ways over a network. Most commonly we hear of TCP/IP, which is exactly the protocol that permits us to communicate between our web browser and Google's servers. An HTTP request typically occurs over a TCP connection and brings us a few significant benefits including guaranteed delivery acknowledgement and error handling. However, the most important aspect of this protocol is its nature of "connectedness". With TCP we know exactly who we're trying to communicate with, the only thing left to do is keep a connection open.
What are WebSockets?
So we know that a socket is made when we need to communicate between applications. We have a high level understanding of the benefits of a TCP connection. Now we can talk about what a WebSocket is!
A WebSocket is a communication protocol that provides full duplex communication over a single TCP connection.
Let's break that statement down with an example.
If we request anything from google.com using HTTP we would make a request to Google's server. Wait. Hopefully we would receive a reply with the information we wanted. Then the connection is closed because we got what we needed.
With WebSockets on the other hand, we make a connection with a server and request for that connection to stay open until it times out or explicitly exited. While that connection is persisted, both the server and client are free to pass messages over the wire. There's no need to request and wait for a reply anymore because we leave the connection open!
It's kind of like passing notes in class versus having a string telephone with two cups on the ends to verbally communicate with someone.
WebSockets allow us to receive live updates in either direction easily. Common use cases include:
- Multiplayer games
- Messaging platforms
- Stock market tickers
- Sports scores
But what's going on??
At a high level there are 5 major requirements of a WebSocket connection 😄
🤝 - Handshake, when a client request to open a WebSocket with a server a negotiation happens with a
HTTP/1.1 101 Switching Protocol. The server and client need to check out that they want to and are allowed to open up to each other.
🔐🔗 - Connection, once the handshake is complete, a connection is made, and a bidirectional stream of data can occur between the client and server.
👂💬 - Messaging, now that the connection is up, both applications can send and receive data to each other! We'll see shortly that the Swift API supports
🏓 - Ping, another important part of WebSockets is the ability to Ping/Pong each other. A client can tell the server "Hey, I know I haven't sent a message in a while, but I'm still around so don't close me off yet!" Pinging isn't directly required by spec but it certainly has it's common use cases.
❌ - Closure, when we're all done, a client can tell the server it wants to close off the connection and whatever reason it may have for doing so.
If you're interested in the full specification of WebSockets please checkout RFC6455, it covers all the nitty gritty details of how WebSockets should be constructed and used.
How could we use WebSockets pre iOS13?
Many developers opt for 3rd party library's:
These are all still acceptable and relevant options for various use cases, but now we can move forward with a truly native Swift solution!
What can we do now!?
- A webSocketTask can be created with a URL from a URLSession.
- In order to kick off the handshake and connection we call
- From there we can use
URLSessionWebSocketDelegatemethods to confirm a successful or nonsuccessful attempt.
- If we want to send a message
.datamethod to wrap up our data to pass to the
- If we want to listen to messages that could be coming in to us we have
task.receivethat gives us a Result type which will either be
.successwith a String or Data message or
.failurewith an error.
- Sending a ping is as simple as calling
task.sendPingand I would advise wrapping a call to that method in a
Timerif you have a WebSocket server that specifies a ping timeout.
- Lastly, we have
.cancelwhich takes a type of cancellation you want and an optional reason.
A more thorough implementation using
URLSessionWebSocketDelegate can be found here.
But there's more!
or less securely:
We now have the ability to go lower level in the Network framework and create our own Swift WebSocket Server or provide custom client side WebSocket implementations. Please view my server implementation based off the Advances in Networking WWDC talk and a wonderful generic TCP Listener found in the credits.
I have made a few projects recently with the new WebSocket API's. To see an end to end implementation please open the WebSocket Workspace. While some of the Apple developer documentation is limited, it is fully usable and I'm excited to keep playing around! Feel free to borrow my implementation of a WebSocket wrapper. Please check out the comparison between Starscream and Apple's implementation of client side WebSockets, (Scarily similar). If you see anything you would like to add, have an issue with, or want to help out in any way, please submit a pull request or issue!