Files
damus/damus/Nostr/WebSocket.swift
Bryan Montz 6bf5293701 Swift cleanup: don't capture case values only to ignore them in switch statements
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00

105 lines
3.0 KiB
Swift

//
// WebSocket.swift
// damus
//
// Created by Bryan Montz on 4/13/23.
//
import Combine
import Foundation
enum WebSocketEvent {
case connected
case message(URLSessionWebSocketTask.Message)
case disconnected(URLSessionWebSocketTask.CloseCode, String?)
case error(Error)
var description: String? {
switch self {
case .connected:
return "Connected"
case .message:
return nil // adding this to the RelayLog was too noisy
case .disconnected(let close_code, let reason):
return "Disconnected: Close code: \(close_code), reason: \(reason ?? "unknown")"
case .error(let error):
return "Error: \(error)"
}
}
}
final class WebSocket: NSObject, URLSessionWebSocketDelegate {
private let url: URL
private let session: URLSession
private lazy var webSocketTask: URLSessionWebSocketTask = {
let task = session.webSocketTask(with: url)
task.delegate = self
return task
}()
let subject = PassthroughSubject<WebSocketEvent, Never>()
init(_ url: URL, session: URLSession = .shared) {
self.url = url
self.session = session
}
func ping(receiveHandler: @escaping (Error?) -> Void) {
self.webSocketTask.sendPing(pongReceiveHandler: receiveHandler)
}
func connect() {
resume()
}
func disconnect(closeCode: URLSessionWebSocketTask.CloseCode = .normalClosure, reason: Data? = nil) {
webSocketTask.cancel(with: closeCode, reason: reason)
// reset after disconnecting to be ready for reconnecting
let task = session.webSocketTask(with: url)
task.delegate = self
webSocketTask = task
let reason_str: String?
if let reason {
reason_str = String(data: reason, encoding: .utf8)
} else {
reason_str = nil
}
subject.send(.disconnected(closeCode, reason_str))
}
func send(_ message: URLSessionWebSocketTask.Message) {
webSocketTask.send(message) { [weak self] error in
if let error {
self?.subject.send(.error(error))
}
}
}
private func resume() {
webSocketTask.receive { [weak self] result in
switch result {
case .success(let message):
self?.subject.send(.message(message))
self?.resume()
case .failure(let error):
self?.subject.send(.error(error))
}
}
webSocketTask.resume()
}
// MARK: - URLSessionWebSocketDelegate
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol theProtocol: String?) {
subject.send(.connected)
}
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
disconnect(closeCode: closeCode, reason: reason)
}
}