Skip to content

Instantly share code, notes, and snippets.

@kylehowells
Created August 3, 2020 02:49
Show Gist options
  • Select an option

  • Save kylehowells/9248bf9733eb294fac7a3c94f1f5b09b to your computer and use it in GitHub Desktop.

Select an option

Save kylehowells/9248bf9733eb294fac7a3c94f1f5b09b to your computer and use it in GitHub Desktop.
Proof on concept WebSocket application comparing the built in NSURLSessionWebSocketTask and the Starscream WebSocket library on Github
//
// ViewController.swift
// WebSocket Echo
//
// Created by Kyle Howells on 02/08/2020.
// Copyright © 2020 Kyle Howells. All rights reserved.
//
import UIKit
import Starscream
class ViewController: UIViewController, UITextFieldDelegate, WebSocketDelegate {
// MARK: View Setup
let textView:UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 16)
tv.isEditable = false
tv.backgroundColor = UIColor(white: 0.95, alpha: 1)
return tv
}()
let textfield:UITextField = {
let tf = UITextField()
tf.backgroundColor = UIColor(white: 0.9, alpha: 1)
return tf
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addSubview(textView)
self.view.addSubview(textfield)
self.textfield.delegate = self
self.textfield.becomeFirstResponder()
textView.text = ""
self.connect()
}
// MARK: Layout
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let size = self.view.bounds.size
self.textfield.frame = {
var f = CGRect.zero
f.size.width = size.width - 20
f.size.height = 30
f.origin.y = size.height - (f.size.height + self.view.safeAreaInsets.bottom + 10)
f.origin.x = 10
return f
}()
self.textView.frame = {
var f = CGRect.zero
f.size.width = size.width
f.size.height = self.textfield.frame.minY - (self.view.safeAreaInsets.top + 8)
f.origin.y = self.view.safeAreaInsets.top
return f
}()
}
// MARK: UITextField
@objc func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if true {
let text = textfield.text ?? ""
self.sendMessage(text)
}
textfield.text = ""
return false
}
func log(_ text:String) {
print(text)
DispatchQueue.main.async {
var existingText = (self.textView.text ?? "")
if existingText.count > 0 {
existingText += "\n"
}
self.textView.text = existingText + text
if self.textView.text.count > 2 {
let range = NSRange(location: (self.textView.text as NSString).length - 2, length: 1)
self.textView.scrollRangeToVisible(range)
}
}
}
// MARK: - Web Sockets
let urlString = "ws://echo.websocket.org"
lazy var socket:WebSocket = {
var request = URLRequest(url: URL(string: urlString)!)
request.setValue("superchat", forHTTPHeaderField: "Sec-WebSocket-Protocol")
request.timeoutInterval = 5
let socket = WebSocket(request: request)
socket.delegate = self
return socket
}()
var isConnected:Bool = false
func connect() {
self.log("Connect to: \(urlString)")
self.socket.connect()
}
func disconnect() {
self.socket.disconnect()
self.log("Disconnect")
}
func sendMessage(_ text:String) {
self.log("Send message: \"\(text)\"")
self.socket.write(string: text)
}
// MARK: WebSocketDelegate
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(let headers):
isConnected = true
self.log("Websocket is connected: \(headers)")
case .disconnected(let reason, let code):
isConnected = false
self.log("websocket is disconnected: \(reason) with code: \(code)")
case .text(let string):
self.log("Received text: \(string)")
case .binary(let data):
self.log("Received data: \(data.count)")
case .ping(let data):
self.log("Ping: \(data)")
case .pong(let data):
self.log("Pong: \(data)")
case .viabilityChanged(let connected):
self.log("Viability Changed: \(connected)")
if connected {
self.socket.write(ping: Data())
}
case .reconnectSuggested(let connect):
self.log("Reconnect Suggested: \(connect)")
case .cancelled:
isConnected = false
self.log("Connection Cancelled")
case .error(let error):
isConnected = false
self.log("Connection error: \(error)")
}
}
}
//
// ViewController.swift
// WebSocket Client
//
// Created by Kyle Howells on 01/08/2020.
// Copyright © 2020 Kyle Howells. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate, URLSessionWebSocketDelegate {
// MARK: View Setup
let textView:UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 16)
tv.isEditable = false
tv.backgroundColor = UIColor(white: 0.95, alpha: 1)
return tv
}()
let textfield:UITextField = {
let tf = UITextField()
tf.backgroundColor = UIColor(white: 0.9, alpha: 1)
return tf
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addSubview(textView)
self.view.addSubview(textfield)
self.textfield.delegate = self
self.textfield.becomeFirstResponder()
textView.text = ""
self.connect()
}
// MARK: Layout
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let size = self.view.bounds.size
self.textfield.frame = {
var f = CGRect.zero
f.size.width = size.width - 20
f.size.height = 30
f.origin.y = size.height - (f.size.height + self.view.safeAreaInsets.bottom + 10)
f.origin.x = 10
return f
}()
self.textView.frame = {
var f = CGRect.zero
f.size.width = size.width
f.size.height = self.textfield.frame.minY - (self.view.safeAreaInsets.top + 8)
f.origin.y = self.view.safeAreaInsets.top
return f
}()
}
// MARK: UITextField
@objc func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if true {
let text = textfield.text ?? ""
self.sendMessage(text)
}
textfield.text = ""
return false
}
func log(_ text:String) {
print(text)
DispatchQueue.main.async {
var existingText = (self.textView.text ?? "")
if existingText.count > 0 {
existingText += "\n"
}
self.textView.text = existingText + text
}
}
// MARK: - Web Sockets
lazy var urlSession: URLSession = {
print(self)
let session = URLSession(configuration: .default, delegate: self, delegateQueue: self.delegateQueue)
print(session)
return session
}()
let delegateQueue = OperationQueue()
let urlString = "ws://echo.websocket.org"
lazy var webSocketTask: URLSessionWebSocketTask = {
return self.urlSession.webSocketTask(with: URL(string: urlString)!) //, protocols: ["protocolOne"])
}()
func connect() {
self.webSocketTask.resume()
self.listen()
}
func disconnect() {
webSocketTask.cancel(with: .goingAway, reason: nil)
}
func listen() {
webSocketTask.receive { result in
switch result {
case .failure(let error):
self.log("Connection error: \(error)")
case .success(let message):
switch message {
case .string(let text):
self.log("Received text message: \"\(text)\"")
case .data(let data):
self.log("Received data message: \"\(data)\"")
@unknown default:
fatalError()
}
self.listen()
}
}
}
func sendMessage(_ text:String) {
self.log("Send message: \"\(text)\"")
webSocketTask.send(URLSessionWebSocketTask.Message.string(text)) { error in
if let error = error {
self.log("Connection error: \(error)")
}
}
}
// MARK: URLSessionDelegate
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol _protocol: String?) {
self.log("Connected to: \(urlString), protocol: \(_protocol ?? "(null)")")
}
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
self.log("Disconnected from: \(urlString)")
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
self.log("Encountered error: \(error)")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment