-
Notifications
You must be signed in to change notification settings - Fork 0
Network: Websocket based
The websocket system in the Stalkr tvOS client use the package Starscream.
In folder NetworkManager/WebSocket/WebSocketChannels
, create a new Swift file. This file specific one new websocket channel to the client interact with the Stalkr User Service.
The examples code in this documentation is how create a simple chat room.
We need specify the possibles events that this websocket channel could to send. To this, create a new enum. Each case
in this enum is one possible event.
For example:
enum ChatWebSocketEvents {
case newMessage(name: String?, text: String?)
case someoneJoined(name: String?)
}
Each websocket channel need one delegate, to use in the controller.
In the Stalkr network layer, we already have a base delegate, called BaseWebSocketDelegate
. The base delegate have a associatedtype EvetEnum
. We need specific this associatedtype with the enum of our new websocket channel.
protocol ChatWebSocketDelegate: BaseWebSocketDelegate {
func newMessage(socket: WebSocket, event: ChatWebSocketEvents)
}
In this moment, we have a enum with the possibles events and a delegate. Now, we need create a class of our new websocket channel.
To create the class:
final class ChatWebSocketChannel<D: ChatWebSocketDelegate>: WebSocketChannel {
let path = "chat" // path in the URL of Stalkr User Service
let headers = ["client-type": "tvOS"] // header sent in each request
var socket: WebSocket!
typealias ChannelDelegate = D
weak var delegate: D?
func didReceiveMessage(_ response: ResponseText) {
...
}
func websocketDidReceiveData(socket: WebSocket, data: Data) {
...
}
}
The function didReceiveMessage(_:)
is called when the websocket receive a new text message. In this function you need write the code to call the delegate with the values received. For example:
func didReceiveMessage(_ response: ResponseText) {
switch response.type {
case "new_message":
let messageName = response.data?["name"]?.stringValue
let messageText = response.data?["text"]?.stringValue
delegate?.newMessage(socket: socket, event: .newMessage(name: messageName, text: messageText))
case "someone_joined":
let userName = response.data?["name"]?.stringValue
delegate?.newMessage(socket: socket, event: .someoneJoined(name: userName))
default:
// we don't expect that will receive a response type different from above
delegate?.unexpectedMessage(socket: socket, unexpectedMessage: .typeUnknow(response))
}
}
We call the function unexpectedMessage(socket:unexpectedMessage:
) on delegate when receive a strange behavior. In this case, if we heave a event with type unexpected.
Now, we need to write the function websocketDidReceiveData(socket:data:)
. In this channel, we will never receive a binary data, then:
func websocketDidReceiveData(socket: WebSocket, data: Data) {
// we don't expect that will receive a binnary data in this channel
delegate?.unexpectedMessage(socket: socket, unexpectedMessage: .unexpectedBinnary(data))
}
Now, as we amazing websocket channel in network layer, we can use it in our controller. For it, the controller need subscriber the protocol ChatWebSocketDelegate
:
class ChatViewController: UIViewController, ChatWebSocketDelegate {
func didConnect(socket: WebSocket) {
...
}
func didDisconnect(socket: WebSocket, error: NSError?) {
...
}
func newMessage(socket: WebSocket, event: ChatWebSocketEvents) {
...
}
func unexpectedMessage(socket: WebSocket, unexpectedMessage: WebSocketUnexpectedMessage) {
...
}
}
Then, we need connect to the server
class ChatViewController: UIViewController, ChatWebSocketDelegate {
var chatChannel: ChatWebSocketChannel<ChatViewController>?
override func viewDidLoad() {
super.viewDidLoad()
chatChannel = ChatWebSocketChannel<ChatViewController>() // 1
chatChannel!.delegate = self
chatChannel!.socket.connect()
}
...
In the liner numbered with 1 could be rewrite with:
let environment = Environment(name: "local", host: "ws://127.0.0.1:13254")
chatChannel = ChatWebSocketChannel<AuthenticationViewController>(environment: environment)
You can see the code about the default environment in the file NetworkManager/DefaultNetwork/DefaultNetwork.swift
.
The BaseWebSocketDelegate
(that the ChatWebSocketDelegate
is heir) have four functions:
didConnect(socket:)
didDisconnect(socket:error:)
newMessage(socket:event:)
unexpectedMessage(socket:unexpectedMessage:)
One very simple example to implement this functions in the controller is:
func didConnect(socket: WebSocket) {
print("chat connect!")
}
func didDisconnect(socket: WebSocket, error: NSError?) {
print("WARNING: Chat's websocket is disconnected: \(error?.localizedDescription ?? "nil")")
}
func newMessage(socket: WebSocket, event: ChatWebSocketEvents) {
switch event {
case .newMessage(let name, let text):
print("\(name) sent the message '\(text)'")
case .someoneJoined(let name):
print("user \(name) entered in room")
}
}
func unexpectedMessage(socket: WebSocket, unexpectedMessage: WebSocketUnexpectedMessage) {
print("WARNING: Unexpected message received in socket!")
}
TODO: Encapsulate the sending of messages.
If you want send a message to the server, you can use
socket.write(string: "foo")
You can see others ways in documentation of the Starscream.