Replace Starscream with URLSessionWebSocketTask
Changelog-Fixed: Fix slow reconnection issues
This commit is contained in:
committed by
William Casarin
parent
6ac68b5a73
commit
0e94c48e26
@@ -1,4 +1,3 @@
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 4),
|
||||
.Package(url: "https://github.com/jb55/secp256k1.swift.git", branch: "main")
|
||||
]
|
||||
|
||||
@@ -216,7 +216,6 @@
|
||||
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEF727F7A08200C66700 /* damusTests.swift */; };
|
||||
4CE6DF0227F7A08200C66700 /* damusUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0127F7A08200C66700 /* damusUITests.swift */; };
|
||||
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; };
|
||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
||||
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
||||
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; };
|
||||
@@ -253,6 +252,7 @@
|
||||
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
|
||||
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50088DA029E8271A008A1FDF /* WebSocket.swift */; };
|
||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
|
||||
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
||||
@@ -669,6 +669,7 @@
|
||||
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
|
||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||
50088DA029E8271A008A1FDF /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
|
||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
|
||||
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
||||
@@ -708,7 +709,6 @@
|
||||
files = (
|
||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */,
|
||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -973,6 +973,7 @@
|
||||
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */,
|
||||
4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */,
|
||||
4C363A8F28247A1D006E126D /* NostrLink.swift */,
|
||||
50088DA029E8271A008A1FDF /* WebSocket.swift */,
|
||||
);
|
||||
path = Nostr;
|
||||
sourceTree = "<group>";
|
||||
@@ -1348,7 +1349,6 @@
|
||||
);
|
||||
name = damus;
|
||||
packageProductDependencies = (
|
||||
4CE6DF1127F7A2B300C66700 /* Starscream */,
|
||||
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
||||
4C06670328FC7EC500038D2A /* Kingfisher */,
|
||||
6C7DE41E2955169800E66263 /* Vault */,
|
||||
@@ -1454,7 +1454,6 @@
|
||||
);
|
||||
mainGroup = 4CE6DEDA27F7A08100C66700;
|
||||
packageReferences = (
|
||||
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
||||
@@ -1661,6 +1660,7 @@
|
||||
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */,
|
||||
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
|
||||
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */,
|
||||
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */,
|
||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
||||
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||
@@ -2259,14 +2259,6 @@
|
||||
revision = 40b4b38b3b1c83f7088c76189a742870e0ca06a9;
|
||||
};
|
||||
};
|
||||
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/daltoniam/Starscream";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 4.0.0;
|
||||
};
|
||||
};
|
||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SparrowTek/Vault";
|
||||
@@ -2288,11 +2280,6 @@
|
||||
package = 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */;
|
||||
productName = secp256k1;
|
||||
};
|
||||
4CE6DF1127F7A2B300C66700 /* Starscream */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */;
|
||||
productName = Starscream;
|
||||
};
|
||||
6C7DE41E2955169800E66263 /* Vault */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
||||
|
||||
@@ -17,15 +17,6 @@
|
||||
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/daltoniam/Starscream",
|
||||
"state" : {
|
||||
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
|
||||
"version" : "4.0.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "vault",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Starscream
|
||||
|
||||
struct TimestampedProfile {
|
||||
let profile: Profile
|
||||
@@ -686,31 +685,6 @@ func get_since_time(last_event: NostrEvent?) -> Int64? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? {
|
||||
switch ev {
|
||||
case .binary(let dat):
|
||||
return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay)
|
||||
case .cancelled:
|
||||
return NostrEvent(content: "cancelled", pubkey: relay)
|
||||
case .connected:
|
||||
return NostrEvent(content: "connected", pubkey: relay)
|
||||
case .disconnected:
|
||||
return NostrEvent(content: "disconnected", pubkey: relay)
|
||||
case .error(let err):
|
||||
return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay)
|
||||
case .text(let txt):
|
||||
return NostrEvent(content: "text \(txt)", pubkey: relay)
|
||||
case .pong:
|
||||
return NostrEvent(content: "pong", pubkey: relay)
|
||||
case .ping:
|
||||
return NostrEvent(content: "ping", pubkey: relay)
|
||||
case .viabilityChanged(let b):
|
||||
return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay)
|
||||
case .reconnectSuggested(let b):
|
||||
return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay)
|
||||
}
|
||||
}
|
||||
|
||||
func is_notification(ev: NostrEvent, pubkey: String) -> Bool {
|
||||
if ev.pubkey == pubkey {
|
||||
return false
|
||||
|
||||
@@ -290,17 +290,12 @@ class HomeModel: ObservableObject {
|
||||
send_home_filters(relay_id: relay_id)
|
||||
}
|
||||
case .error(let merr):
|
||||
let desc = merr.debugDescription
|
||||
let desc = String(describing: merr)
|
||||
if desc.contains("Software caused connection abort") {
|
||||
pool.reconnect(to: [relay_id])
|
||||
}
|
||||
case .disconnected: fallthrough
|
||||
case .cancelled:
|
||||
case .disconnected:
|
||||
pool.reconnect(to: [relay_id])
|
||||
case .reconnectSuggested(let t):
|
||||
if t {
|
||||
pool.reconnect(to: [relay_id])
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -52,14 +52,12 @@ class Relay: Identifiable {
|
||||
let descriptor: RelayDescriptor
|
||||
let connection: RelayConnection
|
||||
|
||||
var last_pong: UInt32
|
||||
var flags: Int
|
||||
|
||||
init(descriptor: RelayDescriptor, connection: RelayConnection) {
|
||||
self.flags = 0
|
||||
self.descriptor = descriptor
|
||||
self.connection = connection
|
||||
self.last_pong = 0
|
||||
}
|
||||
|
||||
func mark_broken() {
|
||||
|
||||
@@ -5,26 +5,22 @@
|
||||
// Created by William Casarin on 2022-04-02.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import Starscream
|
||||
|
||||
enum NostrConnectionEvent {
|
||||
case ws_event(WebSocketEvent)
|
||||
case nostr_event(NostrResponse)
|
||||
}
|
||||
|
||||
final class RelayConnection: WebSocketDelegate {
|
||||
final class RelayConnection {
|
||||
private(set) var isConnected = false
|
||||
private(set) var isConnecting = false
|
||||
private(set) var isReconnecting = false
|
||||
|
||||
private(set) var last_connection_attempt: TimeInterval = 0
|
||||
private lazy var socket = {
|
||||
let req = URLRequest(url: url)
|
||||
let socket = WebSocket(request: req, compressionHandler: .none)
|
||||
socket.delegate = self
|
||||
return socket
|
||||
}()
|
||||
private lazy var socket = WebSocket(url)
|
||||
private var subscriptionToken: AnyCancellable?
|
||||
|
||||
private var handleEvent: (NostrConnectionEvent) -> ()
|
||||
private let url: URL
|
||||
|
||||
@@ -33,16 +29,6 @@ final class RelayConnection: WebSocketDelegate {
|
||||
self.handleEvent = handleEvent
|
||||
}
|
||||
|
||||
func reconnect() {
|
||||
if isConnected {
|
||||
isReconnecting = true
|
||||
disconnect()
|
||||
} else {
|
||||
// we're already disconnected, so just connect
|
||||
connect(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
func connect(force: Bool = false) {
|
||||
if !force && (isConnected || isConnecting) {
|
||||
return
|
||||
@@ -50,11 +36,27 @@ final class RelayConnection: WebSocketDelegate {
|
||||
|
||||
isConnecting = true
|
||||
last_connection_attempt = Date().timeIntervalSince1970
|
||||
|
||||
subscriptionToken = socket.subject
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
self?.receive(event: .error(error))
|
||||
case .finished:
|
||||
self?.receive(event: .disconnected(.normalClosure, nil))
|
||||
}
|
||||
} receiveValue: { [weak self] event in
|
||||
self?.receive(event: event)
|
||||
}
|
||||
|
||||
socket.connect()
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
socket.disconnect()
|
||||
subscriptionToken = nil
|
||||
|
||||
isConnected = false
|
||||
isConnecting = false
|
||||
}
|
||||
@@ -64,34 +66,46 @@ final class RelayConnection: WebSocketDelegate {
|
||||
print("failed to encode nostr req: \(req)")
|
||||
return
|
||||
}
|
||||
|
||||
socket.write(string: req)
|
||||
socket.send(.string(req))
|
||||
}
|
||||
|
||||
// MARK: - WebSocketDelegate
|
||||
|
||||
func didReceive(event: WebSocketEvent, client: WebSocket) {
|
||||
private func receive(event: WebSocketEvent) {
|
||||
switch event {
|
||||
case .connected:
|
||||
self.isConnected = true
|
||||
self.isConnecting = false
|
||||
|
||||
case .disconnected:
|
||||
self.isConnecting = false
|
||||
self.isConnected = false
|
||||
if self.isReconnecting {
|
||||
self.isReconnecting = false
|
||||
self.connect()
|
||||
case .message(let message):
|
||||
self.receive(message: message)
|
||||
case .disconnected(let closeCode, let reason):
|
||||
if closeCode != .normalClosure {
|
||||
print("⚠️ Warning: RelayConnection (\(self.url)) closed with code \(closeCode), reason: \(String(describing: reason))")
|
||||
}
|
||||
|
||||
case .cancelled, .error:
|
||||
self.isConnecting = false
|
||||
self.isConnected = false
|
||||
|
||||
case .text(let txt):
|
||||
if txt.utf8.count > 2000 {
|
||||
isConnected = false
|
||||
isConnecting = false
|
||||
reconnect()
|
||||
case .error(let error):
|
||||
print("⚠️ Warning: RelayConnection (\(self.url)) error: \(error)")
|
||||
isConnected = false
|
||||
isConnecting = false
|
||||
reconnect()
|
||||
}
|
||||
self.handleEvent(.ws_event(event))
|
||||
}
|
||||
|
||||
func reconnect() {
|
||||
guard !isConnecting else {
|
||||
return // we're already trying to connect
|
||||
}
|
||||
disconnect()
|
||||
connect()
|
||||
}
|
||||
|
||||
private func receive(message: URLSessionWebSocketTask.Message) {
|
||||
switch message {
|
||||
case .string(let messageString):
|
||||
if messageString.utf8.count > 2000 {
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
if let ev = decode_nostr_event(txt: txt) {
|
||||
if let ev = decode_nostr_event(txt: messageString) {
|
||||
DispatchQueue.main.async {
|
||||
self.handleEvent(.nostr_event(ev))
|
||||
}
|
||||
@@ -99,18 +113,18 @@ final class RelayConnection: WebSocketDelegate {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let ev = decode_nostr_event(txt: txt) {
|
||||
if let ev = decode_nostr_event(txt: messageString) {
|
||||
handleEvent(.nostr_event(ev))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
break
|
||||
case .data(let messageData):
|
||||
if let messageString = String(data: messageData, encoding: .utf8) {
|
||||
receive(message: .string(messageString))
|
||||
}
|
||||
@unknown default:
|
||||
print("An unexpected URLSessionWebSocketTask.Message was received.")
|
||||
}
|
||||
|
||||
handleEvent(.ws_event(event))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
struct SubscriptionId: Identifiable, CustomStringConvertible {
|
||||
let id: String
|
||||
@@ -44,7 +45,24 @@ class RelayPool {
|
||||
var request_queue: [QueuedRequest] = []
|
||||
var seen: Set<String> = Set()
|
||||
var counts: [String: UInt64] = [:]
|
||||
|
||||
private let network_monitor = NWPathMonitor()
|
||||
private let network_monitor_queue = DispatchQueue(label: "io.damus.network_monitor")
|
||||
private var last_network_status: NWPath.Status = .unsatisfied
|
||||
|
||||
init() {
|
||||
network_monitor.pathUpdateHandler = { [weak self] path in
|
||||
if (path.status == .satisfied || path.status == .requiresConnection) && self?.last_network_status != path.status {
|
||||
DispatchQueue.main.async {
|
||||
self?.connect_to_disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
self?.last_network_status = path.status
|
||||
}
|
||||
network_monitor.start(queue: network_monitor_queue)
|
||||
}
|
||||
|
||||
var descriptors: [RelayDescriptor] {
|
||||
relays.map { $0.descriptor }
|
||||
}
|
||||
@@ -106,11 +124,11 @@ class RelayPool {
|
||||
for relay in relays {
|
||||
let c = relay.connection
|
||||
|
||||
let is_connecting = c.isReconnecting || c.isConnecting
|
||||
let is_connecting = c.isConnecting
|
||||
|
||||
if is_connecting && (Date.now.timeIntervalSince1970 - c.last_connection_attempt) > 5 {
|
||||
print("stale connection detected (\(relay.descriptor.url.absoluteString)). retrying...")
|
||||
relay.connection.connect(force: true)
|
||||
relay.connection.reconnect()
|
||||
} else if relay.is_broken || is_connecting || c.isConnected {
|
||||
continue
|
||||
} else {
|
||||
@@ -208,19 +226,6 @@ class RelayPool {
|
||||
relays.first(where: { $0.id == id })
|
||||
}
|
||||
|
||||
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
|
||||
if case .ws_event(let ws_event) = event {
|
||||
if case .pong = ws_event {
|
||||
for relay in relays {
|
||||
if relay.id == relay_id {
|
||||
relay.last_pong = UInt32(Date.now.timeIntervalSince1970)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func run_queue(_ relay_id: String) {
|
||||
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
|
||||
guard req.relay == relay_id else {
|
||||
@@ -250,7 +255,6 @@ class RelayPool {
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
||||
record_last_pong(relay_id: relay_id, event: event)
|
||||
record_seen(relay_id: relay_id, event: event)
|
||||
|
||||
// run req queue when we reconnect
|
||||
|
||||
87
damus/Nostr/WebSocket.swift
Normal file
87
damus/Nostr/WebSocket.swift
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ struct RelayStatus: View {
|
||||
if c.isConnected {
|
||||
conn_image = "network"
|
||||
conn_color = .green
|
||||
} else if c.isConnecting || c.isReconnecting {
|
||||
} else if c.isConnecting {
|
||||
connecting = true
|
||||
} else {
|
||||
conn_image = "exclamationmark.circle.fill"
|
||||
|
||||
@@ -127,7 +127,7 @@ struct SaveKeysView: View {
|
||||
|
||||
case .error(let err):
|
||||
self.loading = false
|
||||
self.error = "\(err.debugDescription)"
|
||||
self.error = String(describing: err)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user