// // NetworkServiceApiWebSockets.swift // Wallet // // Created by Saveliy Stavitsky on 1/15/21. // Copyright © 2021 AM. All rights reserved. // import Foundation import Starscream extension Notification.Name { static let socketDidConnect = Self("Network.Service.WebSockets.socketDidConnect") static let socketDidUpdateValue = Self("Network.Service.WebSockets.socketDidUpdateValue") static let socketDidDisconnect = Self("Network.Service.WebSockets.socketDidDisconnect") } extension Network.Service { final class WebSockets: Common.Service.Provider { private var timer: Timer? private var reconectTimer: Timer? private var collection = [String: Subscription]() init() { self.timer = delayed(45, repeats: true) { self.update() } self.reconectTimer = delayed(1, repeats: true) { self.reconect() } } } } extension Network.Service.WebSockets { func open(subscription: String, id: String) { self.collection[id] = Subscription(id: id, subscription: subscription) } func contains(id: String) -> Bool { self.collection[id].isExist } func remove(id: String) { self.collection[id] = nil } func remove(idPrefix prefix: String) { self.collection .filter { $0.key.hasPrefix(prefix) } .map { $0.key } .forEach { self.collection[$0] = nil } } func remove(idSuffix suffix: String) { self.collection .filter { $0.key.hasSuffix(suffix) } .map { $0.key } .forEach { self.collection[$0] = nil } } func removeAll() { self.collection = [:] } } extension Network.Service.WebSockets { private func update() { self.collection.forEach { subscription in if !subscription.value.socket.isConnected { subscription.value.socket.connect() } else { subscription.value.socket.write(ping: Data()) } } } private func reconect() { self.collection .filter { !$0.value.socket.isConnected } .forEach { $0.value.socket.connect() } } } extension Network.Service.WebSockets { fileprivate final class Subscription { let socket: WebSocket let id: String let subscription: String? init(id: String, subscription: String?) { self.id = id self.subscription = subscription let environment = ApplicationEnvironment.shared().current var request = URLRequest(url: URL(string: environment.backend.webSocket)!) request.timeoutInterval = 5 environment.headers.forEach { (key, value) in request.addValue(value, forHTTPHeaderField: key) } self.socket = WebSocket(request: request) self.socket.advancedDelegate = self self.socket.connect() } deinit { self.socket.disconnect() } } } extension Network.Service.WebSockets.Subscription: WebSocketAdvancedDelegate { func websocketDidConnect(socket: WebSocket) { // print("websocketDidConnect") if let subscription = self.subscription { socket.write(string: subscription) } Notification.post(name: .socketDidConnect, userInfo: ["id": self.id]) } func websocketDidDisconnect(socket: WebSocket, error: Error?) { // print("websocketDidDisconnect: \(error)") // socket.connect() // do not reconnect continuously Notification.post(name: .socketDidDisconnect, userInfo: ["id": self.id]) } func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse) { Notification.post(name: .socketDidUpdateValue, userInfo: ["id": self.id, "data": text]) } func websocketDidReceiveData(socket: WebSocket, data: Data, response: WebSocket.WSResponse) { // print("websocketDidReceiveData: \(response.code)") Notification.post(name: .socketDidUpdateValue, userInfo: ["id": self.id, "data": String(data: data, encoding: .utf8) ?? ""]) } func websocketHttpUpgrade(socket: WebSocket, request: String) { // print("websocketHttpUpgrade: \(request)") } func websocketHttpUpgrade(socket: WebSocket, response: String) { // print("websocketHttpUpgrade: \(response)") } }