WebSocket 近幾年越來越熱門,Apple 也在 WWDC 2019 上發表 iOS 13 內建 WebSocket 套件。本章將介紹如何使用 URLSessionWebSocketTask
,以及如何使用第三方套件 Starsream,來實現 WebSocket 串接。
URLSessionWebSocketTask
URLSessionWebSocketTask
是 iOS 13 之後才有,所以要先確定 Xcode 的 iOS Deployment Target 是設在 13 之後。
建立連線
宣告 URLRequest(url:)
,然後呼叫 URLSession.webSocketTask(with:)
。它會回傳一個 URLSessionWebSocketTask
,然後呼叫 Task 的 .resume()
來建立連線。是不是很簡單呢?
guard let url = URL(string: "wss://echo.websocket.org/") else { print("Error: can not create URL") return } let request = URLRequest(url: url) let webSocketTask = URLSession.shared.webSocketTask(with: request) webSocketTask.resume()
傳送訊息
把訊息字串放入 URLSessionWebSocketTask.Message
裡,然後呼叫 URLSessionWebSocketTask.send()
來傳送訊息。
let message = URLSessionWebSocketTask.Message.string("Hello WebSocket") webSocketTask.send(message) { error in if let error = error { print(error) } }
接收訊息
呼叫 URLSessionWebSocketTask.receive()
來接收訊息,並傳給它處理接受到的訊息的 Handler。收到的訊息也是 URLSessionWebSocketTask.Message
型態,要把訊息字串從裡面提出來。
要注意的是,當你呼叫一次 .receive()
,它就只會接收一次訊息。所以在 Hanlder 處理完訊息後,離開之前,要記得再呼叫一次 .receive()
。
private func receive() { webSocketTask.receive { result in switch result { case .success(let message): switch message { case .string(let text): print("Received string: \(text)") case .data(let data): print("Received data: \(data)") @unknown default: fatalError() } case .failure(let error): print(error) } self.receive() } }
斷開連線
呼叫 URLSessionWebSocketTask.cancel()
就可以斷開連線。
webSocketTask.cancel(with: .goingAway, reason: nil)
監聽連線狀態
如果你要監聽連線狀態的話,就要實作 URLSessionWebSocketDelegate
。它包含了兩個監聽事件,一個是當連線建立時會被觸發,另外一個則是當連線斷開時會被觸發。
extension URLSessionWebSocket: URLSessionWebSocketDelegate { public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { print("URLSessionWebSocketTask is connected") } public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { let reasonString: String if let reason = reason, let string = String(data: reason, encoding: .utf8) { reasonString = string } else { reasonString = "" } print("URLSessionWebSocketTask is closed: code=\(closeCode), reason=\(reasonString)") } }
URLSessionWebSocketDelegate
必須一開始就設給 URLSession
。所以建立連線時要自己宣告一個 URLSession
,而不可以用 URLSession.defualt
。
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: .main) let request = URLRequest(url: url) webSocketTask = urlSession.webSocketTask(with: request) webSocketTask.resume()
Starsream
URLSessionWebSocketTask
看似不錯使用,但是它有些問題。第一個是,它只有在 iOS 13 之後才支援。iOS 13 目前還是太新,我們希望還沒有升級的 iPhone 使用者也可以安裝我們的 App。另一個是,URLSessionWebSocketTask
在接收訊息和監聽事件的方式上,並不是很方便。Starsream 可以為我們解決上述的問題。
首先要先安裝 Starsram 套件,如果你是使用 CocoaPods 的話,將這行放入你的 Podfile 裡。
pod 'Starscream', '~> 4.0.0'
如果你不熟悉 CocoaPods 操作的話,可以參考這篇。
建立連線
宣告一個 WebSocket
並傳入 URL,然後呼叫 .connect()
就可以建立連線了。
import Starscream guard let url = URL(string: "wss://echo.websocket.org/") else { print("Error: can not create URL") return } let request = URLRequest(url: url) webSocket = WebSocket(request: request) webSocket.connect()
傳送訊息
呼叫 WebSocket.write(string:)
即可傳送訊息。這跟 URLSessionWebSocketTask
相比,真的簡單很多!
webSocket.write(string: message)
接收訊息和監聽連線狀態
Starscream 處理訊息接收和連線狀態,都是使用事件 (Event) 的方式。尤其是訊息接收,與 URLSessionWebSocketTask
相比,事件的方式的確比較直覺。
webSocket.delegate = self extension StarscreamWebSocket: WebSocketDelegate { func didReceive(event: WebSocketEvent, client: WebSocket) { switch event { case .connected(_): print("WebSocket is connected") case .disconnected(let reason, let code): print("Disconnected: code=\(code), reason=\(reason)") case .text(let message): print("Received: \(message)") case .binary(_): break case .pong(_): break case .ping(_): break case .error(let error): print(error ?? "") case .viabilityChanged(_): break case .reconnectSuggested(_): break case .cancelled: print("WebSocket is cancelled") } } }
斷開連線
呼叫 WebSocket.disconnect()
就可以斷開連線。
webSocket.disconnect()
結語
我們介紹了如何使用 URLSessionWebSocketTask
和 Starscream 來實現 WebSocket。這兩個方式相比起來,我們會推薦 Starscream。雖然 URLSessionWebSocketTask
是 iOS SDK 內建的,但是其使用的方式和普及性,遠遠不及 Starscream。