Created
August 3, 2020 02:49
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // 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)") | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // 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