From 24849d55ef408bcbca6d2d68126d1292de5766ff Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 3 Feb 2022 09:10:45 +0100 Subject: [PATCH 01/28] Add Automatic connection handler --- Sources/Relayer/AppStateObserving.swift | 33 ++++++++ Sources/Relayer/Dispatching.swift | 76 ++++++++++++++++++- .../WalletConnect/WalletConnectClient.swift | 3 +- 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 Sources/Relayer/AppStateObserving.swift diff --git a/Sources/Relayer/AppStateObserving.swift b/Sources/Relayer/AppStateObserving.swift new file mode 100644 index 000000000..e053f177c --- /dev/null +++ b/Sources/Relayer/AppStateObserving.swift @@ -0,0 +1,33 @@ + +import Foundation +import UIKit + +protocol AppStateObserving { + var onWillEnterForeground: (()->())? {get set} + var onWillEnterBackground: (()->())? {get set} +} + +class AppStateObserver: AppStateObserving { + @objc var onWillEnterForeground: (() -> ())? + + @objc var onWillEnterBackground: (() -> ())? + + init() { + subscribeNotificationCenter() + } + + private func subscribeNotificationCenter() { +#if os(iOS) + NotificationCenter.default.addObserver( + self, + selector: #selector(getter: onWillEnterForeground), + name: UIApplication.willEnterForegroundNotification, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(getter: onWillEnterBackground), + name: UIApplication.willResignActiveNotification, + object: nil) +#endif + } +} diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index 742d92154..5f26ac869 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -23,7 +23,8 @@ final class Dispatcher: NSObject, Dispatching { init(url: URL, networkMonitor: NetworkMonitoring = NetworkMonitor(), socket: WebSocketSessionProtocol, - socketConnectionObserver: SocketConnectionObserving) { + socketConnectionObserver: SocketConnectionObserving, + socketConnectionHandler: SocketConnectionHandler) { self.url = url self.networkMonitor = networkMonitor self.socket = socket @@ -38,6 +39,7 @@ final class Dispatcher: NSObject, Dispatching { func send(_ string: String, completion: @escaping (Error?) -> Void) { if socket.isConnected { self.socket.send(string, completionHandler: completion) + //TODO - enqueue if fails } else { textFramesQueue.enqueue(string) } @@ -51,7 +53,6 @@ final class Dispatcher: NSObject, Dispatching { func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { socket.disconnect(with: closeCode) - onDisconnect?() } private func setUpWebSocketSession() { @@ -94,3 +95,74 @@ final class Dispatcher: NSObject, Dispatching { } } + +protocol SocketConnectionHandler { + var appStateObserver: AppStateObserving {get} + func handleConnect() throws + func handleDisconnect() throws + func handleNetworkUnsatisfied() throws + func handleNetworkSatisfied(_ url: URL) +} + + +import UIKit +class AutomaticSocketConnectionHandler: SocketConnectionHandler { + enum Error: Swift.Error { + case manualSocketConnectionForbidden + case manualSocketDisconnectionForbidden + } + var appStateObserver: AppStateObserving + let socket: WebSocketSessionProtocol + let url: URL + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid + + init(socket: WebSocketSessionProtocol, url: URL, appStateObserver: AppStateObserving = AppStateObserver()) { + self.appStateObserver = appStateObserver + self.socket = socket + self.url = url + setUpStateObserving() + } + + private func setUpStateObserving() { + appStateObserver.onWillEnterBackground = { + + } + appStateObserver.onWillEnterForeground = { [unowned self] in + socket.connect(on: url) + } + } + + func registerBackgroundTask() { +#if os(iOS) + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in + self?.endBackgroundTask() + } +#endif + } + + func endBackgroundTask() { +#if os(iOS) + socket.disconnect(with: .normalClosure) + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid +#endif + } + + func handleConnect() throws { + throw Error.manualSocketConnectionForbidden + } + + func handleDisconnect() throws { + throw Error.manualSocketDisconnectionForbidden + } + + func handleNetworkUnsatisfied() { + socket.disconnect(with: .goingAway) + } + + func handleNetworkSatisfied(_ url: URL) { + if !socket.isConnected { + socket.connect(on: url) + } + } +} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index ddb239ff8..9cdb21226 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -84,8 +84,7 @@ public final class WalletConnectClient { func endBackgroundTask() { #if os(iOS) - wakuRelay.disconnect(closeCode: .goingAway) - print("Background task ended.") + wakuRelay.disconnect(closeCode: .normalClosure) UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid #endif From eb2b227a713906d0174a4b0e048c311691bcc386 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 3 Feb 2022 09:36:20 +0100 Subject: [PATCH 02/28] add socket connection handler to dispatcher --- .../AutomaticSocketConnectionHandler.swift | 70 ++++++++++++++ Example/ExampleApp.xcodeproj/project.pbxproj | 2 + Sources/Relayer/Dispatching.swift | 91 ++----------------- Sources/Relayer/WakuNetworkRelay.swift | 5 +- Sources/Relayer/WebSocketSession.swift | 9 +- 5 files changed, 90 insertions(+), 87 deletions(-) create mode 100644 Example/AutomaticSocketConnectionHandler.swift diff --git a/Example/AutomaticSocketConnectionHandler.swift b/Example/AutomaticSocketConnectionHandler.swift new file mode 100644 index 000000000..751232746 --- /dev/null +++ b/Example/AutomaticSocketConnectionHandler.swift @@ -0,0 +1,70 @@ + +import UIKit +import Foundation + +protocol SocketConnectionHandler { + var appStateObserver: AppStateObserving {get} + func handleConnect() throws + func handleDisconnect() throws + func handleNetworkUnsatisfied() + func handleNetworkSatisfied() +} + +class AutomaticSocketConnectionHandler: SocketConnectionHandler { + enum Error: Swift.Error { + case manualSocketConnectionForbidden + case manualSocketDisconnectionForbidden + } + var appStateObserver: AppStateObserving + let socket: WebSocketSessionProtocol + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid + + init(socket: WebSocketSessionProtocol, appStateObserver: AppStateObserving = AppStateObserver()) { + self.appStateObserver = appStateObserver + self.socket = socket + setUpStateObserving() + } + + private func setUpStateObserving() { + appStateObserver.onWillEnterBackground = { + + } + appStateObserver.onWillEnterForeground = { [unowned self] in + socket.connect() + } + } + + func registerBackgroundTask() { +#if os(iOS) + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in + self?.endBackgroundTask() + } +#endif + } + + func endBackgroundTask() { +#if os(iOS) + socket.disconnect(with: .normalClosure) + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid +#endif + } + + func handleConnect() throws { + throw Error.manualSocketConnectionForbidden + } + + func handleDisconnect() throws { + throw Error.manualSocketDisconnectionForbidden + } + + func handleNetworkUnsatisfied() { + socket.disconnect(with: .goingAway) + } + + func handleNetworkSatisfied() { + if !socket.isConnected { + socket.connect() + } + } +} diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 5bd25f858..6db2712f5 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -95,6 +95,7 @@ 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsViewController.swift; sourceTree = ""; }; 8460DD012750D7020081F94C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.swift; sourceTree = ""; }; + 8468599427ABCB4900128D10 /* AutomaticSocketConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticSocketConnectionHandler.swift; sourceTree = ""; }; 84CE641C27981DED00142511 /* DApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 84CE641E27981DED00142511 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84CE642027981DED00142511 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -182,6 +183,7 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( + 8468599427ABCB4900128D10 /* AutomaticSocketConnectionHandler.swift */, 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 84CE641D27981DED00142511 /* DApp */, diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index 5f26ac869..9f468d51f 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -16,24 +16,23 @@ final class Dispatcher: NSObject, Dispatching { var onMessage: ((String) -> ())? private var textFramesQueue = Queue() private var networkMonitor: NetworkMonitoring - private let url: URL var socket: WebSocketSessionProtocol var socketConnectionObserver: SocketConnectionObserving + var socketConnectionHandler: SocketConnectionHandler - init(url: URL, - networkMonitor: NetworkMonitoring = NetworkMonitor(), + init(networkMonitor: NetworkMonitoring = NetworkMonitor(), socket: WebSocketSessionProtocol, socketConnectionObserver: SocketConnectionObserving, socketConnectionHandler: SocketConnectionHandler) { - self.url = url self.networkMonitor = networkMonitor self.socket = socket self.socketConnectionObserver = socketConnectionObserver + self.socketConnectionHandler = socketConnectionHandler super.init() setUpWebSocketSession() setUpSocketConnectionObserving() setUpNetworkMonitoring() - socket.connect(on: url) + socket.connect() } func send(_ string: String, completion: @escaping (Error?) -> Void) { @@ -46,13 +45,13 @@ final class Dispatcher: NSObject, Dispatching { } func connect() { - if !socket.isConnected { - socket.connect(on: url) - } + //todo handle error + try! socketConnectionHandler.handleConnect() } func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { - socket.disconnect(with: closeCode) + //todo handle error + try! socketConnectionHandler.handleDisconnect() } private func setUpWebSocketSession() { @@ -76,10 +75,10 @@ final class Dispatcher: NSObject, Dispatching { private func setUpNetworkMonitoring() { networkMonitor.onSatisfied = { [weak self] in - self?.connect() + self?.socketConnectionHandler.handleNetworkSatisfied() } networkMonitor.onUnsatisfied = { [weak self] in - self?.disconnect(closeCode: .goingAway) + self?.socketConnectionHandler.handleNetworkUnsatisfied() } networkMonitor.startMonitoring() } @@ -96,73 +95,3 @@ final class Dispatcher: NSObject, Dispatching { } -protocol SocketConnectionHandler { - var appStateObserver: AppStateObserving {get} - func handleConnect() throws - func handleDisconnect() throws - func handleNetworkUnsatisfied() throws - func handleNetworkSatisfied(_ url: URL) -} - - -import UIKit -class AutomaticSocketConnectionHandler: SocketConnectionHandler { - enum Error: Swift.Error { - case manualSocketConnectionForbidden - case manualSocketDisconnectionForbidden - } - var appStateObserver: AppStateObserving - let socket: WebSocketSessionProtocol - let url: URL - private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid - - init(socket: WebSocketSessionProtocol, url: URL, appStateObserver: AppStateObserving = AppStateObserver()) { - self.appStateObserver = appStateObserver - self.socket = socket - self.url = url - setUpStateObserving() - } - - private func setUpStateObserving() { - appStateObserver.onWillEnterBackground = { - - } - appStateObserver.onWillEnterForeground = { [unowned self] in - socket.connect(on: url) - } - } - - func registerBackgroundTask() { -#if os(iOS) - backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in - self?.endBackgroundTask() - } -#endif - } - - func endBackgroundTask() { -#if os(iOS) - socket.disconnect(with: .normalClosure) - UIApplication.shared.endBackgroundTask(backgroundTaskID) - backgroundTaskID = .invalid -#endif - } - - func handleConnect() throws { - throw Error.manualSocketConnectionForbidden - } - - func handleDisconnect() throws { - throw Error.manualSocketDisconnectionForbidden - } - - func handleNetworkUnsatisfied() { - socket.disconnect(with: .goingAway) - } - - func handleNetworkSatisfied(_ url: URL) { - if !socket.isConnected { - socket.connect(on: url) - } - } -} diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 7477a759c..26b018d8d 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -47,8 +47,9 @@ public final class WakuNetworkRelay { uniqueIdentifier: String) { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) - let socket = WebSocketSession(session: urlSession) - let dispatcher = Dispatcher(url: url, socket: socket, socketConnectionObserver: socketConnectionObserver) + let socket = WebSocketSession(session: urlSession, url: url) + let socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) + let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: socketConnectionHandler) self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage, diff --git a/Sources/Relayer/WebSocketSession.swift b/Sources/Relayer/WebSocketSession.swift index 85c4bc7f4..5eaf4d897 100644 --- a/Sources/Relayer/WebSocketSession.swift +++ b/Sources/Relayer/WebSocketSession.swift @@ -4,7 +4,7 @@ protocol WebSocketSessionProtocol { var onMessageReceived: ((String) -> ())? {get set} var onMessageError: ((Error) -> ())? {get set} var isConnected: Bool {get} - func connect(on url: URL) + func connect() func disconnect(with closeCode: URLSessionWebSocketTask.CloseCode) func send(_ message: String, completionHandler: @escaping ((Error?) -> Void)) } @@ -13,7 +13,7 @@ protocol WebSocketSessionProtocol { final class WebSocketSession: NSObject, WebSocketSessionProtocol { var onMessageReceived: ((String) -> ())? var onMessageError: ((Error) -> ())? - + let url: URL var isConnected: Bool { webSocketTask != nil } @@ -22,12 +22,13 @@ final class WebSocketSession: NSObject, WebSocketSessionProtocol { private var webSocketTask: URLSessionWebSocketTaskProtocol? - init(session: URLSessionProtocol) { + init(session: URLSessionProtocol, url: URL) { self.session = session + self.url = url super.init() } - func connect(on url: URL) { + func connect() { webSocketTask = session.webSocketTask(with: url) listen() webSocketTask?.resume() From 6dcf93b1347d77a99f043cdc7a7b5c24bb9bbfd3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 3 Feb 2022 10:00:21 +0100 Subject: [PATCH 03/28] remove socket connection handling from wallet connect client --- Example/ExampleApp.xcodeproj/project.pbxproj | 2 - Sources/Relayer/AppStateObserving.swift | 15 +++++- .../AutomaticSocketConnectionHandler.swift | 5 +- Sources/Relayer/Dispatching.swift | 2 - .../WalletConnect/WalletConnectClient.swift | 47 ------------------- 5 files changed, 16 insertions(+), 55 deletions(-) rename {Example => Sources/Relayer}/AutomaticSocketConnectionHandler.swift (94%) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 6db2712f5..5bd25f858 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -95,7 +95,6 @@ 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsViewController.swift; sourceTree = ""; }; 8460DD012750D7020081F94C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.swift; sourceTree = ""; }; - 8468599427ABCB4900128D10 /* AutomaticSocketConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticSocketConnectionHandler.swift; sourceTree = ""; }; 84CE641C27981DED00142511 /* DApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 84CE641E27981DED00142511 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84CE642027981DED00142511 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -183,7 +182,6 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( - 8468599427ABCB4900128D10 /* AutomaticSocketConnectionHandler.swift */, 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 84CE641D27981DED00142511 /* DApp */, diff --git a/Sources/Relayer/AppStateObserving.swift b/Sources/Relayer/AppStateObserving.swift index e053f177c..c2b1954f2 100644 --- a/Sources/Relayer/AppStateObserving.swift +++ b/Sources/Relayer/AppStateObserving.swift @@ -20,14 +20,25 @@ class AppStateObserver: AppStateObserving { #if os(iOS) NotificationCenter.default.addObserver( self, - selector: #selector(getter: onWillEnterForeground), + selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver( self, - selector: #selector(getter: onWillEnterBackground), + selector: #selector(appWillEnterBackground), name: UIApplication.willResignActiveNotification, object: nil) #endif } + + @objc + private func appWillEnterBackground() { + onWillEnterBackground?() + } + + @objc + private func appWillEnterForeground() { + onWillEnterForeground?() + } + } diff --git a/Example/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/AutomaticSocketConnectionHandler.swift similarity index 94% rename from Example/AutomaticSocketConnectionHandler.swift rename to Sources/Relayer/AutomaticSocketConnectionHandler.swift index 751232746..43ba4a9b4 100644 --- a/Example/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/AutomaticSocketConnectionHandler.swift @@ -26,9 +26,10 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } private func setUpStateObserving() { - appStateObserver.onWillEnterBackground = { - + appStateObserver.onWillEnterBackground = { [unowned self] in + registerBackgroundTask() } + appStateObserver.onWillEnterForeground = { [unowned self] in socket.connect() } diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index 9f468d51f..adbb038c0 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -93,5 +93,3 @@ final class Dispatcher: NSObject, Dispatching { } } } - - diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 9cdb21226..bbe91e2d7 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -31,9 +31,6 @@ public final class WalletConnectClient { private let secureStorage: SecureStorage private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated) private let history: JsonRpcHistory -#if os(iOS) - private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid -#endif // MARK: - Initializers @@ -70,27 +67,6 @@ public final class WalletConnectClient { self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) setUpEnginesCallbacks() - subscribeNotificationCenter() - registerBackgroundTask() - } - - func registerBackgroundTask() { -#if os(iOS) - backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in - self?.endBackgroundTask() - } -#endif - } - - func endBackgroundTask() { -#if os(iOS) - wakuRelay.disconnect(closeCode: .normalClosure) - UIApplication.shared.endBackgroundTask(backgroundTaskID) - backgroundTaskID = .invalid -#endif - } - deinit { - unsubscribeNotificationCenter() } // MARK: - Public interface @@ -327,27 +303,4 @@ public final class WalletConnectClient { ) delegate?.didReceive(sessionProposal: sessionProposal) } - - private func subscribeNotificationCenter() { -#if os(iOS) - NotificationCenter.default.addObserver( - self, - selector: #selector(appWillEnterForeground), - name: UIApplication.willEnterForegroundNotification, - object: nil) -#endif - } - - private func unsubscribeNotificationCenter() { -#if os(iOS) - NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil) -#endif - } - - @objc - private func appWillEnterForeground() { - wakuRelay.connect() - registerBackgroundTask() - } - } From 17753fe48fcf08d52efb4fdcb2259328cb96201c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 3 Feb 2022 14:52:02 +0100 Subject: [PATCH 04/28] add manual and automatic socket connection type --- Sources/Relayer/AppStateObserving.swift | 4 ++++ Sources/Relayer/Dispatching.swift | 15 ++++++--------- Sources/Relayer/NetworkMonitoring.swift | 1 + ...ndler.swift => SocketConnectionHandler.swift} | 7 ++++++- Sources/Relayer/SocketConnectionObserving.swift | 3 +++ Sources/Relayer/SocketConnectionType.swift | 7 +++++++ Sources/Relayer/WakuNetworkRelay.swift | 16 +++++++++------- .../WalletConnect/Relay/NetworkRelaying.swift | 4 ++-- Sources/WalletConnect/WalletConnectClient.swift | 14 +++++++++++--- 9 files changed, 49 insertions(+), 22 deletions(-) rename Sources/Relayer/{AutomaticSocketConnectionHandler.swift => SocketConnectionHandler.swift} (96%) create mode 100644 Sources/Relayer/SocketConnectionType.swift diff --git a/Sources/Relayer/AppStateObserving.swift b/Sources/Relayer/AppStateObserving.swift index c2b1954f2..777658cd0 100644 --- a/Sources/Relayer/AppStateObserving.swift +++ b/Sources/Relayer/AppStateObserving.swift @@ -1,6 +1,8 @@ import Foundation +#if os(iOS) import UIKit +#endif protocol AppStateObserving { var onWillEnterForeground: (()->())? {get set} @@ -42,3 +44,5 @@ class AppStateObserver: AppStateObserving { } } + + diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index adbb038c0..2479010bf 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -6,8 +6,8 @@ protocol Dispatching { var onDisconnect: (()->())? {get set} var onMessage: ((String) -> ())? {get set} func send(_ string: String, completion: @escaping (Error?)->()) - func connect() - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) + func connect() throws + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws } final class Dispatcher: NSObject, Dispatching { @@ -32,7 +32,6 @@ final class Dispatcher: NSObject, Dispatching { setUpWebSocketSession() setUpSocketConnectionObserving() setUpNetworkMonitoring() - socket.connect() } func send(_ string: String, completion: @escaping (Error?) -> Void) { @@ -44,14 +43,12 @@ final class Dispatcher: NSObject, Dispatching { } } - func connect() { - //todo handle error - try! socketConnectionHandler.handleConnect() + func connect() throws { + try socketConnectionHandler.handleConnect() } - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { - //todo handle error - try! socketConnectionHandler.handleDisconnect() + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + try socketConnectionHandler.handleDisconnect() } private func setUpWebSocketSession() { diff --git a/Sources/Relayer/NetworkMonitoring.swift b/Sources/Relayer/NetworkMonitoring.swift index 99acd096b..61bbdd5ab 100644 --- a/Sources/Relayer/NetworkMonitoring.swift +++ b/Sources/Relayer/NetworkMonitoring.swift @@ -26,3 +26,4 @@ class NetworkMonitor: NetworkMonitoring { monitor.start(queue: monitorQueue) } } + diff --git a/Sources/Relayer/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler.swift similarity index 96% rename from Sources/Relayer/AutomaticSocketConnectionHandler.swift rename to Sources/Relayer/SocketConnectionHandler.swift index 43ba4a9b4..2d9fbd51e 100644 --- a/Sources/Relayer/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler.swift @@ -1,5 +1,7 @@ +#if os(iOS) import UIKit +#endif import Foundation protocol SocketConnectionHandler { @@ -17,12 +19,15 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } var appStateObserver: AppStateObserving let socket: WebSocketSessionProtocol +#if os(iOS) private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid - +#endif + init(socket: WebSocketSessionProtocol, appStateObserver: AppStateObserving = AppStateObserver()) { self.appStateObserver = appStateObserver self.socket = socket setUpStateObserving() + socket.connect() } private func setUpStateObserving() { diff --git a/Sources/Relayer/SocketConnectionObserving.swift b/Sources/Relayer/SocketConnectionObserving.swift index bb5ca1cfb..41f83e249 100644 --- a/Sources/Relayer/SocketConnectionObserving.swift +++ b/Sources/Relayer/SocketConnectionObserving.swift @@ -18,3 +18,6 @@ class SocketConnectionObserver: NSObject, URLSessionWebSocketDelegate, SocketCon onDisconnect?() } } + + + diff --git a/Sources/Relayer/SocketConnectionType.swift b/Sources/Relayer/SocketConnectionType.swift new file mode 100644 index 000000000..bab534860 --- /dev/null +++ b/Sources/Relayer/SocketConnectionType.swift @@ -0,0 +1,7 @@ + +import Foundation + +public enum SocketConnectionType { + case automatic + case manual +} diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 26b018d8d..f329b8ded 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -33,7 +33,8 @@ public final class WakuNetworkRelay { init(dispatcher: Dispatching, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, - uniqueIdentifier: String) { + uniqueIdentifier: String, + socketConnectionType: SocketConnectionType) { self.logger = logger self.dispatcher = dispatcher let historyIdentifier = "com.walletconnect.sdk.\(uniqueIdentifier).relayer.subscription_json_rpc_record" @@ -44,7 +45,7 @@ public final class WakuNetworkRelay { public convenience init(logger: ConsoleLogging, url: URL, keyValueStorage: KeyValueStorage, - uniqueIdentifier: String) { + uniqueIdentifier: String, socketConnectionType: SocketConnectionType) { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) @@ -53,15 +54,16 @@ public final class WakuNetworkRelay { self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage, - uniqueIdentifier: uniqueIdentifier) + uniqueIdentifier: uniqueIdentifier, + socketConnectionType: socketConnectionType) } - public func connect() { - dispatcher.connect() + public func connect() throws { + try dispatcher.connect() } - public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { - dispatcher.disconnect(closeCode: closeCode) + public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + try dispatcher.disconnect(closeCode: closeCode) } @discardableResult public func publish(topic: String, payload: String, completion: @escaping ((Error?) -> ())) -> Int64 { diff --git a/Sources/WalletConnect/Relay/NetworkRelaying.swift b/Sources/WalletConnect/Relay/NetworkRelaying.swift index 5e734874c..19ceef3fb 100644 --- a/Sources/WalletConnect/Relay/NetworkRelaying.swift +++ b/Sources/WalletConnect/Relay/NetworkRelaying.swift @@ -7,8 +7,8 @@ extension WakuNetworkRelay: NetworkRelaying {} protocol NetworkRelaying { var onConnect: (()->())? {get set} var onMessage: ((_ topic: String, _ message: String) -> ())? {get set} - func connect() - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) + func connect() throws + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws /// - returns: request id @discardableResult func publish(topic: String, payload: String, completion: @escaping ((Error?)->())) -> Int64 /// - returns: request id diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index bbe91e2d7..0f6209caa 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -45,11 +45,11 @@ public final class WalletConnectClient { /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { self.metadata = metadata self.isController = isController self.logger = logger @@ -57,7 +57,7 @@ public final class WalletConnectClient { self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) - self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") + self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "", socketConnectionType: socketConnectionType) let serializer = JSONRPCSerializer(crypto: crypto) self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) @@ -246,6 +246,14 @@ public final class WalletConnectClient { let request = WalletConnectUtils.JsonRpcRecord.Request(method: payload.request.method, params: payload.request.params) return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) } + + public func connectWebSocket() throws { + try wakuRelay.connect() + } + + public func disconnectWebSocket() throws { + try wakuRelay.disconnect(closeCode: .normalClosure) + } // MARK: - Private From 2179835c8232b1dd4b9e7542a9bd14f73ead567e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 09:37:08 +0100 Subject: [PATCH 05/28] Add manual socket connection handler --- Sources/Relayer/Dispatching.swift | 2 +- .../AutomaticSocketConnectionHandler.swift} | 10 +------ .../ManualSocketConnectionHandler.swift | 27 +++++++++++++++++++ .../SocketConnectionHandler.swift | 10 +++++++ .../WalletConnect/WalletConnectClient.swift | 4 +++ 5 files changed, 43 insertions(+), 10 deletions(-) rename Sources/Relayer/{SocketConnectionHandler.swift => SocketConnectionHandler/AutomaticSocketConnectionHandler.swift} (87%) create mode 100644 Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift create mode 100644 Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index 2479010bf..fdd648b1e 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -48,7 +48,7 @@ final class Dispatcher: NSObject, Dispatching { } func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { - try socketConnectionHandler.handleDisconnect() + try socketConnectionHandler.handleDisconnect(closeCode: closeCode) } private func setUpWebSocketSession() { diff --git a/Sources/Relayer/SocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift similarity index 87% rename from Sources/Relayer/SocketConnectionHandler.swift rename to Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 2d9fbd51e..138212c16 100644 --- a/Sources/Relayer/SocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -4,14 +4,6 @@ import UIKit #endif import Foundation -protocol SocketConnectionHandler { - var appStateObserver: AppStateObserving {get} - func handleConnect() throws - func handleDisconnect() throws - func handleNetworkUnsatisfied() - func handleNetworkSatisfied() -} - class AutomaticSocketConnectionHandler: SocketConnectionHandler { enum Error: Swift.Error { case manualSocketConnectionForbidden @@ -60,7 +52,7 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { throw Error.manualSocketConnectionForbidden } - func handleDisconnect() throws { + func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { throw Error.manualSocketDisconnectionForbidden } diff --git a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift new file mode 100644 index 000000000..a94cbbafb --- /dev/null +++ b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -0,0 +1,27 @@ + +import Foundation + +class ManualSocketConnectionHandler: SocketConnectionHandler { + var socket: WebSocketSessionProtocol + + init(socket: WebSocketSessionProtocol) { + self.socket = socket + } + + func handleConnect() throws { + socket.connect() + } + + func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + socket.disconnect(with: closeCode) + } + + func handleNetworkUnsatisfied() { + return + } + + func handleNetworkSatisfied() { + return + } + +} diff --git a/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift new file mode 100644 index 000000000..fa819dd84 --- /dev/null +++ b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift @@ -0,0 +1,10 @@ + +import Foundation + +protocol SocketConnectionHandler { + var socket: WebSocketSessionProtocol {get} + func handleConnect() throws + func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws + func handleNetworkUnsatisfied() + func handleNetworkSatisfied() +} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 0f6209caa..a2c784309 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -247,10 +247,14 @@ public final class WalletConnectClient { return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) } + /// Connect Web Socket in manual socket connection control + /// - Throws: Throws in automatic socket connection control public func connectWebSocket() throws { try wakuRelay.connect() } + /// Disconnect Web Socket in manual socket connection control + /// - Throws: Throws in automatic socket connection control public func disconnectWebSocket() throws { try wakuRelay.disconnect(closeCode: .normalClosure) } From fdc11fc1ee7929e2aa1329bf4ef7f61e010a46fb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 10:23:51 +0100 Subject: [PATCH 06/28] Fix Tests --- .../AutomaticSocketConnectionHandler.swift | 8 +++++ .../ManualSocketConnectionHandler.swift | 1 - Tests/RelayerTests/DispatcherTests.swift | 24 +++----------- .../ManualSocketConnectionHandlerTests.swift | 33 +++++++++++++++++++ .../Mocks/WebSocketSessionMock.swift | 2 +- Tests/RelayerTests/RelayerEndToEndTests.swift | 9 +++-- Tests/RelayerTests/WakuRelayTests.swift | 2 +- .../RelayerTests/WebSocketSessionTests.swift | 22 ++++++------- 8 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 138212c16..01a45d3fe 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -66,3 +66,11 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } } } + +protocol BackgroundTaskRegistering { + func register(name: String, completion: ()->()) +} + +class BackgroundTaskRegistrar { + +} diff --git a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift index a94cbbafb..181fedc5e 100644 --- a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -23,5 +23,4 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { func handleNetworkSatisfied() { return } - } diff --git a/Tests/RelayerTests/DispatcherTests.swift b/Tests/RelayerTests/DispatcherTests.swift index f4d969589..196ec6fcc 100644 --- a/Tests/RelayerTests/DispatcherTests.swift +++ b/Tests/RelayerTests/DispatcherTests.swift @@ -12,35 +12,21 @@ final class DispatcherTests: XCTestCase { webSocketSession = WebSocketSessionMock() networkMonitor = NetworkMonitoringMock() socketConnectionObserver = SocketConnectionObserverMock() - let url = URL(string: "ws://staging.walletconnect.org")! - sut = Dispatcher(url: url, networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver) + sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) } - - func testDisconnectOnConnectionLoss() { - XCTAssertTrue(sut.socket.isConnected) - networkMonitor.onUnsatisfied?() - XCTAssertFalse(sut.socket.isConnected) - } - - func testConnectsOnConnectionSatisfied() { - sut.disconnect(closeCode: .normalClosure) - XCTAssertFalse(sut.socket.isConnected) - networkMonitor.onSatisfied?() - XCTAssertTrue(sut.socket.isConnected) - } - + func testSendWhileConnected() { - sut.connect() + try! sut.connect() sut.send("1"){_ in} XCTAssertEqual(webSocketSession.sendCallCount, 1) } func testTextFramesSentAfterReconnectingSocket() { - sut.disconnect(closeCode: .normalClosure) + try! sut.disconnect(closeCode: .normalClosure) sut.send("1"){_ in} sut.send("2"){_ in} XCTAssertEqual(webSocketSession.sendCallCount, 0) - sut.connect() + try! sut.connect() socketConnectionObserver.onConnect?() XCTAssertEqual(webSocketSession.sendCallCount, 2) } diff --git a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift new file mode 100644 index 000000000..af86bcf51 --- /dev/null +++ b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift @@ -0,0 +1,33 @@ + + +import Foundation +import XCTest +@testable import Relayer + +final class ManualSocketConnectionHandlerTests: XCTestCase { + var sut: Dispatcher! + var webSocketSession: WebSocketSessionMock! + var networkMonitor: NetworkMonitoringMock! + var socketConnectionObserver: SocketConnectionObserverMock! + override func setUp() { + webSocketSession = WebSocketSessionMock() + networkMonitor = NetworkMonitoringMock() + socketConnectionObserver = SocketConnectionObserverMock() + sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) + } + +// func testDisconnectOnConnectionLoss() { +// try! sut.connect() +// XCTAssertTrue(sut.socket.isConnected) +// networkMonitor.onUnsatisfied?() +// XCTAssertFalse(sut.socket.isConnected) +// } +// +// func testConnectsOnConnectionSatisfied() { +// try! sut.disconnect(closeCode: .normalClosure) +// XCTAssertFalse(sut.socket.isConnected) +// networkMonitor.onSatisfied?() +// XCTAssertTrue(sut.socket.isConnected) +// } +// +} diff --git a/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift b/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift index a8f46f4d2..ff0c0cb46 100644 --- a/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift +++ b/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift @@ -10,7 +10,7 @@ class WebSocketSessionMock: WebSocketSessionProtocol { var sendCallCount: Int = 0 var isConnected: Bool = false - func connect(on url: URL) { + func connect() { isConnected = true onConnect?() } diff --git a/Tests/RelayerTests/RelayerEndToEndTests.swift b/Tests/RelayerTests/RelayerEndToEndTests.swift index 3ff516080..a211c5fdb 100644 --- a/Tests/RelayerTests/RelayerEndToEndTests.swift +++ b/Tests/RelayerTests/RelayerEndToEndTests.swift @@ -15,13 +15,14 @@ final class RelayerEndToEndTests: XCTestCase { let logger = ConsoleLogger() let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) - let socket = WebSocketSession(session: urlSession) - let dispatcher = Dispatcher(url: url, socket: socket, socketConnectionObserver: socketConnectionObserver) - return WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") + let socket = WebSocketSession(session: urlSession, url: url) + let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket)) + return WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "", socketConnectionType: .manual) } func testSubscribe() { let relayer = makeRelayer() + try! relayer.connect() let subscribeExpectation = expectation(description: "subscribe call succeeds") relayer.subscribe(topic: "qwerty") { error in XCTAssertNil(error) @@ -33,6 +34,8 @@ final class RelayerEndToEndTests: XCTestCase { func testEndToEndPayload() { let relayA = makeRelayer() let relayB = makeRelayer() + try! relayA.connect() + try! relayB.connect() let randomTopic = String.randomTopic() let payloadA = "A" diff --git a/Tests/RelayerTests/WakuRelayTests.swift b/Tests/RelayerTests/WakuRelayTests.swift index 8eca88978..1b341a856 100644 --- a/Tests/RelayerTests/WakuRelayTests.swift +++ b/Tests/RelayerTests/WakuRelayTests.swift @@ -12,7 +12,7 @@ class WakuRelayTests: XCTestCase { override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - wakuRelay = WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") + wakuRelay = WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "", socketConnectionType: .manual) } override func tearDown() { diff --git a/Tests/RelayerTests/WebSocketSessionTests.swift b/Tests/RelayerTests/WebSocketSessionTests.swift index 97436c340..c7163a305 100644 --- a/Tests/RelayerTests/WebSocketSessionTests.swift +++ b/Tests/RelayerTests/WebSocketSessionTests.swift @@ -7,11 +7,12 @@ final class WebSocketSessionTests: XCTestCase { var webSocketTaskMock: URLSessionWebSocketTaskMock! var sessionMock: URLSessionMock! - + var url: URL! override func setUp() { webSocketTaskMock = URLSessionWebSocketTaskMock() sessionMock = URLSessionMock(webSocketTaskMock: webSocketTaskMock) - sut = WebSocketSession(session: sessionMock) + url = URL.stub() + sut = WebSocketSession(session: sessionMock, url: url) } override func tearDown() { @@ -25,16 +26,15 @@ final class WebSocketSessionTests: XCTestCase { } func testConnect() { - let expectedURL = URL.stub() - sut.connect(on: expectedURL) + sut.connect() XCTAssertTrue(sut.isConnected) XCTAssertTrue(webSocketTaskMock.didCallResume) XCTAssertTrue(webSocketTaskMock.didCallReceive) - XCTAssertEqual(sessionMock.lastSessionTaskURL, expectedURL) + XCTAssertEqual(sessionMock.lastSessionTaskURL, url) } func testDisconnect() { - sut.connect(on: URL.stub()) + sut.connect() sut.disconnect() XCTAssertFalse(sut.isConnected) XCTAssertTrue(webSocketTaskMock.didCallCancel) @@ -43,7 +43,7 @@ final class WebSocketSessionTests: XCTestCase { func testSendMessageSuccessCallbacksNoError() { let expectedMessage = "message" - sut.connect(on: URL.stub()) + sut.connect() sut.send(expectedMessage) { error in XCTAssertNil(error) } @@ -64,7 +64,7 @@ final class WebSocketSessionTests: XCTestCase { func testSendMessageFailure() { webSocketTaskMock.sendMessageError = NSError.mock() - sut.connect(on: URL.stub()) + sut.connect() sut.send("") { error in XCTAssertNotNil(error) XCTAssert(error?.asNetworkError?.isSendMessageError == true) @@ -78,7 +78,7 @@ final class WebSocketSessionTests: XCTestCase { sut.onMessageReceived = { callbackMessage = $0 } webSocketTaskMock.receiveMessageResult = .success(.string(expectedMessage)) - sut.connect(on: URL.stub()) + sut.connect() XCTAssertEqual(callbackMessage, expectedMessage) XCTAssert(webSocketTaskMock.receiveCallsCount == 2) @@ -91,7 +91,7 @@ final class WebSocketSessionTests: XCTestCase { sut.onMessageError = { _ in didCallbackError = true } webSocketTaskMock.receiveMessageResult = .success(.data("message".data(using: .utf8)!)) - sut.connect(on: URL.stub()) + sut.connect() XCTAssertNil(callbackMessage) XCTAssertFalse(didCallbackError) @@ -105,7 +105,7 @@ final class WebSocketSessionTests: XCTestCase { } webSocketTaskMock.receiveMessageResult = .failure(NSError.mock()) - sut.connect(on: URL.stub()) + sut.connect() XCTAssert(webSocketTaskMock.receiveCallsCount == 2) } From 5b53903084e91c3607f944116a9230ca3932808d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 10:32:43 +0100 Subject: [PATCH 07/28] move network monitoring to automatic socket connection handler --- Sources/Relayer/Dispatching.swift | 16 +--------------- .../AutomaticSocketConnectionHandler.swift | 17 ++++++++++++++++- .../ManualSocketConnectionHandler.swift | 8 -------- .../SocketConnectionHandler.swift | 2 -- Tests/RelayerTests/DispatcherTests.swift | 2 +- .../ManualSocketConnectionHandlerTests.swift | 4 ++-- 6 files changed, 20 insertions(+), 29 deletions(-) diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index fdd648b1e..24903af2d 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -15,23 +15,19 @@ final class Dispatcher: NSObject, Dispatching { var onDisconnect: (() -> ())? var onMessage: ((String) -> ())? private var textFramesQueue = Queue() - private var networkMonitor: NetworkMonitoring var socket: WebSocketSessionProtocol var socketConnectionObserver: SocketConnectionObserving var socketConnectionHandler: SocketConnectionHandler - init(networkMonitor: NetworkMonitoring = NetworkMonitor(), - socket: WebSocketSessionProtocol, + init(socket: WebSocketSessionProtocol, socketConnectionObserver: SocketConnectionObserving, socketConnectionHandler: SocketConnectionHandler) { - self.networkMonitor = networkMonitor self.socket = socket self.socketConnectionObserver = socketConnectionObserver self.socketConnectionHandler = socketConnectionHandler super.init() setUpWebSocketSession() setUpSocketConnectionObserving() - setUpNetworkMonitoring() } func send(_ string: String, completion: @escaping (Error?) -> Void) { @@ -70,16 +66,6 @@ final class Dispatcher: NSObject, Dispatching { } } - private func setUpNetworkMonitoring() { - networkMonitor.onSatisfied = { [weak self] in - self?.socketConnectionHandler.handleNetworkSatisfied() - } - networkMonitor.onUnsatisfied = { [weak self] in - self?.socketConnectionHandler.handleNetworkUnsatisfied() - } - networkMonitor.startMonitoring() - } - private func dequeuePendingTextFrames() { while let frame = textFramesQueue.dequeue() { send(frame) { error in diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 01a45d3fe..7f754c91b 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -11,14 +11,19 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } var appStateObserver: AppStateObserving let socket: WebSocketSessionProtocol + private var networkMonitor: NetworkMonitoring #if os(iOS) private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid #endif - init(socket: WebSocketSessionProtocol, appStateObserver: AppStateObserving = AppStateObserver()) { + init(networkMonitor: NetworkMonitoring = NetworkMonitor(), + socket: WebSocketSessionProtocol, + appStateObserver: AppStateObserving = AppStateObserver()) { self.appStateObserver = appStateObserver self.socket = socket + self.networkMonitor = networkMonitor setUpStateObserving() + setUpNetworkMonitoring() socket.connect() } @@ -32,6 +37,16 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } } + private func setUpNetworkMonitoring() { + networkMonitor.onSatisfied = { [weak self] in + self?.handleNetworkSatisfied() + } + networkMonitor.onUnsatisfied = { [weak self] in + self?.handleNetworkUnsatisfied() + } + networkMonitor.startMonitoring() + } + func registerBackgroundTask() { #if os(iOS) backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in diff --git a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift index 181fedc5e..15e7ac618 100644 --- a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -15,12 +15,4 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { socket.disconnect(with: closeCode) } - - func handleNetworkUnsatisfied() { - return - } - - func handleNetworkSatisfied() { - return - } } diff --git a/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift index fa819dd84..32c6a74ea 100644 --- a/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift @@ -5,6 +5,4 @@ protocol SocketConnectionHandler { var socket: WebSocketSessionProtocol {get} func handleConnect() throws func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws - func handleNetworkUnsatisfied() - func handleNetworkSatisfied() } diff --git a/Tests/RelayerTests/DispatcherTests.swift b/Tests/RelayerTests/DispatcherTests.swift index 196ec6fcc..7d51ceeee 100644 --- a/Tests/RelayerTests/DispatcherTests.swift +++ b/Tests/RelayerTests/DispatcherTests.swift @@ -12,7 +12,7 @@ final class DispatcherTests: XCTestCase { webSocketSession = WebSocketSessionMock() networkMonitor = NetworkMonitoringMock() socketConnectionObserver = SocketConnectionObserverMock() - sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) + sut = Dispatcher(socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) } func testSendWhileConnected() { diff --git a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift index af86bcf51..33a09119a 100644 --- a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift +++ b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift @@ -5,7 +5,7 @@ import XCTest @testable import Relayer final class ManualSocketConnectionHandlerTests: XCTestCase { - var sut: Dispatcher! + var sut: ManualSocketConnectionHandler! var webSocketSession: WebSocketSessionMock! var networkMonitor: NetworkMonitoringMock! var socketConnectionObserver: SocketConnectionObserverMock! @@ -13,7 +13,7 @@ final class ManualSocketConnectionHandlerTests: XCTestCase { webSocketSession = WebSocketSessionMock() networkMonitor = NetworkMonitoringMock() socketConnectionObserver = SocketConnectionObserverMock() - sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) +// sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) } // func testDisconnectOnConnectionLoss() { From 5eaeb15bdfe0a9243933d67427340a8977dd6909 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 11:01:17 +0100 Subject: [PATCH 08/28] Add manual socket connection tests --- .../ManualSocketConnectionHandlerTests.swift | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift index 33a09119a..f11a99b4b 100644 --- a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift +++ b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift @@ -11,23 +11,19 @@ final class ManualSocketConnectionHandlerTests: XCTestCase { var socketConnectionObserver: SocketConnectionObserverMock! override func setUp() { webSocketSession = WebSocketSessionMock() - networkMonitor = NetworkMonitoringMock() - socketConnectionObserver = SocketConnectionObserverMock() -// sut = Dispatcher(networkMonitor: networkMonitor, socket: webSocketSession, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: webSocketSession)) + sut = ManualSocketConnectionHandler(socket: webSocketSession) } -// func testDisconnectOnConnectionLoss() { -// try! sut.connect() -// XCTAssertTrue(sut.socket.isConnected) -// networkMonitor.onUnsatisfied?() -// XCTAssertFalse(sut.socket.isConnected) -// } -// -// func testConnectsOnConnectionSatisfied() { -// try! sut.disconnect(closeCode: .normalClosure) -// XCTAssertFalse(sut.socket.isConnected) -// networkMonitor.onSatisfied?() -// XCTAssertTrue(sut.socket.isConnected) -// } -// + func testHandleDisconnect() { + webSocketSession.connect() + XCTAssertTrue(webSocketSession.isConnected) + try! sut.handleDisconnect(closeCode: .normalClosure) + XCTAssertFalse(webSocketSession.isConnected) + } + + func testHandleConnect() { + XCTAssertFalse(webSocketSession.isConnected) + try! sut.handleConnect() + XCTAssertTrue(webSocketSession.isConnected) + } } From 6c0f6e746c356eaee95d72bd857d171711f94f3b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 13:02:35 +0100 Subject: [PATCH 09/28] add automatic socket connection handler tests --- .../AutomaticSocketConnectionHandler.swift | 2 +- ...utomaticSocketConnectionHandlerTests.swift | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 7f754c91b..950e78dcc 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -9,7 +9,7 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { case manualSocketConnectionForbidden case manualSocketDisconnectionForbidden } - var appStateObserver: AppStateObserving + private var appStateObserver: AppStateObserving let socket: WebSocketSessionProtocol private var networkMonitor: NetworkMonitoring #if os(iOS) diff --git a/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift new file mode 100644 index 000000000..918e8ade5 --- /dev/null +++ b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift @@ -0,0 +1,60 @@ + +import Foundation +import XCTest +@testable import Relayer + +class AppStateObserverMock: AppStateObserving { + var onWillEnterForeground: (() -> ())? + var onWillEnterBackground: (() -> ())? +} + +final class AutomaticSocketConnectionHandlerTests: XCTestCase { + var sut: AutomaticSocketConnectionHandler! + var webSocketSession: WebSocketSessionMock! + var networkMonitor: NetworkMonitoringMock! + var socketConnectionObserver: SocketConnectionObserverMock! + var appStateObserver: AppStateObserving! + override func setUp() { + webSocketSession = WebSocketSessionMock() + networkMonitor = NetworkMonitoringMock() + appStateObserver = AppStateObserverMock() + socketConnectionObserver = SocketConnectionObserverMock() + sut = AutomaticSocketConnectionHandler(networkMonitor: networkMonitor, socket: webSocketSession, appStateObserver: appStateObserver) + } + + func testDisconnectOnConnectionLoss() { + webSocketSession.connect() + XCTAssertTrue(sut.socket.isConnected) + networkMonitor.onUnsatisfied?() + XCTAssertFalse(sut.socket.isConnected) + } + + func testConnectsOnConnectionSatisfied() { + webSocketSession.disconnect(with: .normalClosure) + XCTAssertFalse(sut.socket.isConnected) + networkMonitor.onSatisfied?() + XCTAssertTrue(sut.socket.isConnected) + } + + func testHandleConnectThrows() { + XCTAssertThrowsError(try sut.handleConnect()) + } + + func testHandleDisconnectThrows() { + XCTAssertThrowsError(try sut.handleDisconnect(closeCode: .normalClosure)) + } + + func testReconnectsOnEnterForeground() { + webSocketSession.disconnect(with: .normalClosure) + appStateObserver.onWillEnterForeground?() + XCTAssertTrue(sut.socket.isConnected) + } + + func testRegisterTaskOnEnterBackground() { + + } + + func testDisconnectOnEndBackgroundTask() { + + } +} From 935a2e236c307ccd7c55071908b4aac125aa4473 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 13:33:16 +0100 Subject: [PATCH 10/28] Update tests for automatic socket connection --- .../AutomaticSocketConnectionHandler.swift | 36 +++++++++++-------- ...utomaticSocketConnectionHandlerTests.swift | 22 +++++++----- .../Mocks/AppStateObserverMock.swift | 8 +++++ .../Mocks/BackgroundTaskRegistrarMock.swift | 10 ++++++ 4 files changed, 53 insertions(+), 23 deletions(-) create mode 100644 Tests/RelayerTests/Mocks/AppStateObserverMock.swift create mode 100644 Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 950e78dcc..fb1fb687d 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -12,16 +12,16 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { private var appStateObserver: AppStateObserving let socket: WebSocketSessionProtocol private var networkMonitor: NetworkMonitoring -#if os(iOS) - private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid -#endif + private let backgroundTaskRegistrar: BackgroundTaskRegistering init(networkMonitor: NetworkMonitoring = NetworkMonitor(), socket: WebSocketSessionProtocol, - appStateObserver: AppStateObserving = AppStateObserver()) { + appStateObserver: AppStateObserving = AppStateObserver(), + backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar()) { self.appStateObserver = appStateObserver self.socket = socket self.networkMonitor = networkMonitor + self.backgroundTaskRegistrar = backgroundTaskRegistrar setUpStateObserving() setUpNetworkMonitoring() socket.connect() @@ -48,19 +48,13 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } func registerBackgroundTask() { -#if os(iOS) - backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in - self?.endBackgroundTask() + backgroundTaskRegistrar.register(name: "Finish Network Tasks") { [unowned self] in + endBackgroundTask() } -#endif } func endBackgroundTask() { -#if os(iOS) socket.disconnect(with: .normalClosure) - UIApplication.shared.endBackgroundTask(backgroundTaskID) - backgroundTaskID = .invalid -#endif } func handleConnect() throws { @@ -83,9 +77,21 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } protocol BackgroundTaskRegistering { - func register(name: String, completion: ()->()) + func register(name: String, completion: @escaping ()->()) } -class BackgroundTaskRegistrar { - +class BackgroundTaskRegistrar: BackgroundTaskRegistering { +#if os(iOS) + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid +#endif + + func register(name: String, completion: @escaping () -> ()) { +#if os(iOS) + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [weak self] in + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid + completion() + } +#endif + } } diff --git a/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift index 918e8ade5..39c1b40f4 100644 --- a/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift +++ b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift @@ -3,23 +3,24 @@ import Foundation import XCTest @testable import Relayer -class AppStateObserverMock: AppStateObserving { - var onWillEnterForeground: (() -> ())? - var onWillEnterBackground: (() -> ())? -} - final class AutomaticSocketConnectionHandlerTests: XCTestCase { var sut: AutomaticSocketConnectionHandler! var webSocketSession: WebSocketSessionMock! var networkMonitor: NetworkMonitoringMock! var socketConnectionObserver: SocketConnectionObserverMock! var appStateObserver: AppStateObserving! + var backgroundTaskRegistrar: BackgroundTaskRegistrarMock! override func setUp() { webSocketSession = WebSocketSessionMock() networkMonitor = NetworkMonitoringMock() appStateObserver = AppStateObserverMock() socketConnectionObserver = SocketConnectionObserverMock() - sut = AutomaticSocketConnectionHandler(networkMonitor: networkMonitor, socket: webSocketSession, appStateObserver: appStateObserver) + backgroundTaskRegistrar = BackgroundTaskRegistrarMock() + sut = AutomaticSocketConnectionHandler( + networkMonitor: networkMonitor, + socket: webSocketSession, + appStateObserver: appStateObserver, + backgroundTaskRegistrar: backgroundTaskRegistrar) } func testDisconnectOnConnectionLoss() { @@ -51,10 +52,15 @@ final class AutomaticSocketConnectionHandlerTests: XCTestCase { } func testRegisterTaskOnEnterBackground() { - + XCTAssertNil(backgroundTaskRegistrar.completion) + appStateObserver.onWillEnterBackground?() + XCTAssertNotNil(backgroundTaskRegistrar.completion) } func testDisconnectOnEndBackgroundTask() { - + appStateObserver.onWillEnterBackground?() + XCTAssertTrue(sut.socket.isConnected) + backgroundTaskRegistrar.completion!() + XCTAssertFalse(sut.socket.isConnected) } } diff --git a/Tests/RelayerTests/Mocks/AppStateObserverMock.swift b/Tests/RelayerTests/Mocks/AppStateObserverMock.swift new file mode 100644 index 000000000..c5d363ddd --- /dev/null +++ b/Tests/RelayerTests/Mocks/AppStateObserverMock.swift @@ -0,0 +1,8 @@ + +import Foundation +@testable import Relayer + +class AppStateObserverMock: AppStateObserving { + var onWillEnterForeground: (() -> ())? + var onWillEnterBackground: (() -> ())? +} diff --git a/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift b/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift new file mode 100644 index 000000000..af86575c7 --- /dev/null +++ b/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift @@ -0,0 +1,10 @@ + +import Foundation +@testable import Relayer + +class BackgroundTaskRegistrarMock: BackgroundTaskRegistering { + var completion: (()->())? + func register(name: String, completion: @escaping () -> ()) { + self.completion = completion + } +} From 1d0c517ff1359d029ed829367976f22db1158b55 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 13:36:49 +0100 Subject: [PATCH 11/28] extract class into a file --- .../AutomaticSocketConnectionHandler.swift | 20 ----------------- .../BackgroundTaskRegistering.swift | 22 +++++++++++++++++++ .../SocketConnectionType.swift | 0 3 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift rename Sources/Relayer/{ => SocketConnectionHandler}/SocketConnectionType.swift (100%) diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index fb1fb687d..2d919c549 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -75,23 +75,3 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } } } - -protocol BackgroundTaskRegistering { - func register(name: String, completion: @escaping ()->()) -} - -class BackgroundTaskRegistrar: BackgroundTaskRegistering { -#if os(iOS) - private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid -#endif - - func register(name: String, completion: @escaping () -> ()) { -#if os(iOS) - backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [weak self] in - UIApplication.shared.endBackgroundTask(backgroundTaskID) - backgroundTaskID = .invalid - completion() - } -#endif - } -} diff --git a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift new file mode 100644 index 000000000..936f6d0fa --- /dev/null +++ b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -0,0 +1,22 @@ + +import Foundation + +protocol BackgroundTaskRegistering { + func register(name: String, completion: @escaping ()->()) +} + +class BackgroundTaskRegistrar: BackgroundTaskRegistering { +#if os(iOS) + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid +#endif + + func register(name: String, completion: @escaping () -> ()) { +#if os(iOS) + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [weak self] in + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid + completion() + } +#endif + } +} diff --git a/Sources/Relayer/SocketConnectionType.swift b/Sources/Relayer/SocketConnectionHandler/SocketConnectionType.swift similarity index 100% rename from Sources/Relayer/SocketConnectionType.swift rename to Sources/Relayer/SocketConnectionHandler/SocketConnectionType.swift From b1707ebaac6089721a15df4ad59c6173b8e7f45b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 13:43:49 +0100 Subject: [PATCH 12/28] fix build --- .../SocketConnectionHandler/BackgroundTaskRegistering.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift index 936f6d0fa..060c11877 100644 --- a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift +++ b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -1,5 +1,6 @@ import Foundation +import UIKit protocol BackgroundTaskRegistering { func register(name: String, completion: @escaping ()->()) @@ -12,7 +13,7 @@ class BackgroundTaskRegistrar: BackgroundTaskRegistering { func register(name: String, completion: @escaping () -> ()) { #if os(iOS) - backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [weak self] in + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [unowned self] in UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid completion() From a6d0ed4da3be4f53ace53a208bb970df37ed7168 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 13:46:52 +0100 Subject: [PATCH 13/28] invalidate task --- .../SocketConnectionHandler/BackgroundTaskRegistering.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift index 060c11877..4e542bcb6 100644 --- a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift +++ b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -13,6 +13,7 @@ class BackgroundTaskRegistrar: BackgroundTaskRegistering { func register(name: String, completion: @escaping () -> ()) { #if os(iOS) + backgroundTaskID = .invalid backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: name) { [unowned self] in UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid From 0f4e3c2888820090aa74af28cb07e30a77f07f8c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 4 Feb 2022 15:00:18 +0100 Subject: [PATCH 14/28] fixes --- Sources/Relayer/Dispatching.swift | 2 +- .../BackgroundTaskRegistering.swift | 2 ++ Sources/Relayer/WakuNetworkRelay.swift | 14 +++++++++----- Sources/WalletConnect/WalletConnectClient.swift | 2 +- Tests/RelayerTests/RelayerEndToEndTests.swift | 2 +- Tests/RelayerTests/WakuRelayTests.swift | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Sources/Relayer/Dispatching.swift b/Sources/Relayer/Dispatching.swift index 24903af2d..7a863ad27 100644 --- a/Sources/Relayer/Dispatching.swift +++ b/Sources/Relayer/Dispatching.swift @@ -52,7 +52,7 @@ final class Dispatcher: NSObject, Dispatching { self?.onMessage?($0) } socket.onMessageError = { error in - print(error) + print("WebSocket Error \(error)") } } diff --git a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift index 4e542bcb6..f36d54e01 100644 --- a/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift +++ b/Sources/Relayer/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -1,6 +1,8 @@ import Foundation +#if os(iOS) import UIKit +#endif protocol BackgroundTaskRegistering { func register(name: String, completion: @escaping ()->()) diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index f329b8ded..3bb4a5a90 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -33,8 +33,7 @@ public final class WakuNetworkRelay { init(dispatcher: Dispatching, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, - uniqueIdentifier: String, - socketConnectionType: SocketConnectionType) { + uniqueIdentifier: String) { self.logger = logger self.dispatcher = dispatcher let historyIdentifier = "com.walletconnect.sdk.\(uniqueIdentifier).relayer.subscription_json_rpc_record" @@ -49,13 +48,18 @@ public final class WakuNetworkRelay { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) - let socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) + var socketConnectionHandler: SocketConnectionHandler + switch socketConnectionType { + case .automatic: + socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) + case .manual: + socketConnectionHandler = ManualSocketConnectionHandler(socket: socket) + } let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: socketConnectionHandler) self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage, - uniqueIdentifier: uniqueIdentifier, - socketConnectionType: socketConnectionType) + uniqueIdentifier: uniqueIdentifier) } public func connect() throws { diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index a2c784309..555a09f43 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -46,7 +46,7 @@ public final class WalletConnectClient { /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { - self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) + self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName, socketConnectionType: socketConnectionType) } init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { diff --git a/Tests/RelayerTests/RelayerEndToEndTests.swift b/Tests/RelayerTests/RelayerEndToEndTests.swift index a211c5fdb..7d72c651e 100644 --- a/Tests/RelayerTests/RelayerEndToEndTests.swift +++ b/Tests/RelayerTests/RelayerEndToEndTests.swift @@ -17,7 +17,7 @@ final class RelayerEndToEndTests: XCTestCase { let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket)) - return WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "", socketConnectionType: .manual) + return WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") } func testSubscribe() { diff --git a/Tests/RelayerTests/WakuRelayTests.swift b/Tests/RelayerTests/WakuRelayTests.swift index 1b341a856..8eca88978 100644 --- a/Tests/RelayerTests/WakuRelayTests.swift +++ b/Tests/RelayerTests/WakuRelayTests.swift @@ -12,7 +12,7 @@ class WakuRelayTests: XCTestCase { override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - wakuRelay = WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "", socketConnectionType: .manual) + wakuRelay = WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") } override func tearDown() { From 1e6ed7f590d2a82eadccfcda091ea6ab16e5dfb4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 7 Feb 2022 10:12:36 +0100 Subject: [PATCH 15/28] fix race condition remove force unwrap in dapp --- Example/DApp/Connect/ConnectViewController.swift | 2 +- Sources/WalletConnect/Subscription/WCSubscribing.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift index 09e000a7d..8009fd9ae 100644 --- a/Example/DApp/Connect/ConnectViewController.swift +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -86,7 +86,7 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "pairing_cell", for: indexPath) - cell.textLabel?.text = activePairings[indexPath.row].peer!.name + cell.textLabel?.text = activePairings[indexPath.row].peer?.name ?? "" return cell } diff --git a/Sources/WalletConnect/Subscription/WCSubscribing.swift b/Sources/WalletConnect/Subscription/WCSubscribing.swift index afad946a2..d236d407c 100644 --- a/Sources/WalletConnect/Subscription/WCSubscribing.swift +++ b/Sources/WalletConnect/Subscription/WCSubscribing.swift @@ -31,7 +31,7 @@ class WCSubscriber: WCSubscribing { func setSubscription(topic: String) { logger.debug("Setting Subscription...") - concurrentQueue.sync { + concurrentQueue.sync(flags: .barrier) { topics.append(topic) } relay.subscribe(topic: topic) @@ -44,7 +44,7 @@ class WCSubscriber: WCSubscribing { } func removeSubscription(topic: String) { - concurrentQueue.sync { + concurrentQueue.sync(flags: .barrier) { topics.removeAll {$0 == topic} } relay.unsubscribe(topic: topic) From 022a7f5d738504dc4f84724d00720cc52f387b64 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 7 Feb 2022 10:27:50 +0100 Subject: [PATCH 16/28] segregate websocket session interfaces --- .../AutomaticSocketConnectionHandler.swift | 4 ++-- .../ManualSocketConnectionHandler.swift | 4 ++-- .../SocketConnectionHandler.swift | 2 +- Sources/Relayer/WebSocketSession.swift | 9 ++++++--- Tests/RelayerTests/Mocks/WebSocketSessionMock.swift | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 2d919c549..947e249a3 100644 --- a/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -10,12 +10,12 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { case manualSocketDisconnectionForbidden } private var appStateObserver: AppStateObserving - let socket: WebSocketSessionProtocol + let socket: WebSocketConnecting private var networkMonitor: NetworkMonitoring private let backgroundTaskRegistrar: BackgroundTaskRegistering init(networkMonitor: NetworkMonitoring = NetworkMonitor(), - socket: WebSocketSessionProtocol, + socket: WebSocketConnecting, appStateObserver: AppStateObserving = AppStateObserver(), backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar()) { self.appStateObserver = appStateObserver diff --git a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift index 15e7ac618..b3f2d2c8c 100644 --- a/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -2,9 +2,9 @@ import Foundation class ManualSocketConnectionHandler: SocketConnectionHandler { - var socket: WebSocketSessionProtocol + var socket: WebSocketConnecting - init(socket: WebSocketSessionProtocol) { + init(socket: WebSocketConnecting) { self.socket = socket } diff --git a/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift index 32c6a74ea..4320b699e 100644 --- a/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift +++ b/Sources/Relayer/SocketConnectionHandler/SocketConnectionHandler.swift @@ -2,7 +2,7 @@ import Foundation protocol SocketConnectionHandler { - var socket: WebSocketSessionProtocol {get} + var socket: WebSocketConnecting {get} func handleConnect() throws func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws } diff --git a/Sources/Relayer/WebSocketSession.swift b/Sources/Relayer/WebSocketSession.swift index 5eaf4d897..d39f9eaec 100644 --- a/Sources/Relayer/WebSocketSession.swift +++ b/Sources/Relayer/WebSocketSession.swift @@ -4,13 +4,16 @@ protocol WebSocketSessionProtocol { var onMessageReceived: ((String) -> ())? {get set} var onMessageError: ((Error) -> ())? {get set} var isConnected: Bool {get} - func connect() - func disconnect(with closeCode: URLSessionWebSocketTask.CloseCode) func send(_ message: String, completionHandler: @escaping ((Error?) -> Void)) } +protocol WebSocketConnecting { + var isConnected: Bool {get} + func connect() + func disconnect(with closeCode: URLSessionWebSocketTask.CloseCode) +} -final class WebSocketSession: NSObject, WebSocketSessionProtocol { +final class WebSocketSession: NSObject, WebSocketSessionProtocol, WebSocketConnecting { var onMessageReceived: ((String) -> ())? var onMessageError: ((Error) -> ())? let url: URL diff --git a/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift b/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift index ff0c0cb46..73534c24d 100644 --- a/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift +++ b/Tests/RelayerTests/Mocks/WebSocketSessionMock.swift @@ -2,7 +2,7 @@ import Foundation @testable import Relayer -class WebSocketSessionMock: WebSocketSessionProtocol { +class WebSocketSessionMock: WebSocketSessionProtocol, WebSocketConnecting { var onConnect: (() -> ())? var onDisconnect: (() -> ())? var onMessageReceived: ((String) -> ())? From 8a0f2c22f308544c4d97463400d5a92e59fdffce Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 7 Feb 2022 10:42:37 +0100 Subject: [PATCH 17/28] revert client interface changes --- Sources/Relayer/WakuNetworkRelay.swift | 3 ++- .../WalletConnect/WalletConnectClient.swift | 20 ++++--------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 3bb4a5a90..9f41a8d45 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -44,7 +44,8 @@ public final class WakuNetworkRelay { public convenience init(logger: ConsoleLogging, url: URL, keyValueStorage: KeyValueStorage, - uniqueIdentifier: String, socketConnectionType: SocketConnectionType) { + uniqueIdentifier: String, + socketConnectionType: SocketConnectionType = .automatic) { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 555a09f43..bbe91e2d7 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -45,11 +45,11 @@ public final class WalletConnectClient { /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { - self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName, socketConnectionType: socketConnectionType) + public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { + init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata self.isController = isController self.logger = logger @@ -57,7 +57,7 @@ public final class WalletConnectClient { self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) - self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "", socketConnectionType: socketConnectionType) + self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") let serializer = JSONRPCSerializer(crypto: crypto) self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) @@ -246,18 +246,6 @@ public final class WalletConnectClient { let request = WalletConnectUtils.JsonRpcRecord.Request(method: payload.request.method, params: payload.request.params) return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) } - - /// Connect Web Socket in manual socket connection control - /// - Throws: Throws in automatic socket connection control - public func connectWebSocket() throws { - try wakuRelay.connect() - } - - /// Disconnect Web Socket in manual socket connection control - /// - Throws: Throws in automatic socket connection control - public func disconnectWebSocket() throws { - try wakuRelay.disconnect(closeCode: .normalClosure) - } // MARK: - Private From b9ac587472ebf1f63b1beba3d5e6efa03bd6b433 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 7 Feb 2022 11:05:29 +0100 Subject: [PATCH 18/28] print data on socket message --- Sources/Relayer/WebSocketSession.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Relayer/WebSocketSession.swift b/Sources/Relayer/WebSocketSession.swift index d39f9eaec..7d1d83688 100644 --- a/Sources/Relayer/WebSocketSession.swift +++ b/Sources/Relayer/WebSocketSession.swift @@ -72,7 +72,9 @@ final class WebSocketSession: NSObject, WebSocketSessionProtocol, WebSocketConne switch message { case .string(let text): onMessageReceived?(text) - default: + case .data(let data): + print("Transport: Unexpected type of message received: \(data)") + @unknown default: print("Transport: Unexpected type of message received") } } From 19f41a53abaa11887e1dfc0d362ee17dd6a0e56d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 09:56:25 +0100 Subject: [PATCH 19/28] Add initializers or waku and wc client --- ...rkRelay.swift => WakuNetworkRelayer.swift} | 8 ++--- .../WalletConnect/Relay/NetworkRelaying.swift | 2 +- .../WalletConnect/WalletConnectClient.swift | 29 ++++++++++++++++--- Tests/RelayerTests/RelayerEndToEndTests.swift | 8 ++--- Tests/RelayerTests/WakuRelayTests.swift | 4 +-- 5 files changed, 36 insertions(+), 15 deletions(-) rename Sources/Relayer/{WakuNetworkRelay.swift => WakuNetworkRelayer.swift} (97%) diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelayer.swift similarity index 97% rename from Sources/Relayer/WakuNetworkRelay.swift rename to Sources/Relayer/WakuNetworkRelayer.swift index 9f41a8d45..2258a91bc 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelayer.swift @@ -4,7 +4,7 @@ import Combine import WalletConnectUtils -public final class WakuNetworkRelay { +public final class WakuNetworkRelayer { enum RelyerError: Error { case subscriptionIdNotFound } @@ -43,8 +43,8 @@ public final class WakuNetworkRelay { public convenience init(logger: ConsoleLogging, url: URL, - keyValueStorage: KeyValueStorage, - uniqueIdentifier: String, + keyValueStorage: KeyValueStorage = UserDefaults.standard, + uniqueIdentifier: String? = nil, socketConnectionType: SocketConnectionType = .automatic) { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) @@ -60,7 +60,7 @@ public final class WakuNetworkRelay { self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage, - uniqueIdentifier: uniqueIdentifier) + uniqueIdentifier: uniqueIdentifier ?? "") } public func connect() throws { diff --git a/Sources/WalletConnect/Relay/NetworkRelaying.swift b/Sources/WalletConnect/Relay/NetworkRelaying.swift index 19ceef3fb..fa1340f67 100644 --- a/Sources/WalletConnect/Relay/NetworkRelaying.swift +++ b/Sources/WalletConnect/Relay/NetworkRelaying.swift @@ -2,7 +2,7 @@ import Foundation import Relayer -extension WakuNetworkRelay: NetworkRelaying {} +extension WakuNetworkRelayer: NetworkRelaying {} protocol NetworkRelaying { var onConnect: (()->())? {get set} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index bbe91e2d7..f8abd0ec8 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -26,7 +26,6 @@ public final class WalletConnectClient { private let pairingEngine: PairingEngine private let sessionEngine: SessionEngine private let relay: WalletConnectRelaying - private let wakuRelay: NetworkRelaying private let crypto: Crypto private let secureStorage: SecureStorage private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated) @@ -56,11 +55,33 @@ public final class WalletConnectClient { // try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) - let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) - self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") + let relayUrl = WakuNetworkRelayer.makeRelayUrl(host: relayHost, projectId: projectId) + let relayer = WakuNetworkRelayer(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") let serializer = JSONRPCSerializer(crypto: crypto) self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) - self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) + self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) + let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) + let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) + self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) + + self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) + setUpEnginesCallbacks() + } + + public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayer: WakuNetworkRelayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + self.init(metadata: metadata, projectId: projectId, isController: isController, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) + } + + init(metadata: AppMetadata, projectId: String, isController: Bool, relayer: WakuNetworkRelayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + self.metadata = metadata + self.isController = isController + self.logger = logger +// try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever + self.crypto = Crypto(keychain: keychain) + self.secureStorage = SecureStorage(keychain: keychain) + let serializer = JSONRPCSerializer(crypto: crypto) + self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) + self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) diff --git a/Tests/RelayerTests/RelayerEndToEndTests.swift b/Tests/RelayerTests/RelayerEndToEndTests.swift index 7d72c651e..8bbe0373f 100644 --- a/Tests/RelayerTests/RelayerEndToEndTests.swift +++ b/Tests/RelayerTests/RelayerEndToEndTests.swift @@ -11,13 +11,13 @@ final class RelayerEndToEndTests: XCTestCase { let url = URL(string: "wss://staging.walletconnect.org")! private var publishers = [AnyCancellable]() - func makeRelayer() -> WakuNetworkRelay { + func makeRelayer(_ uniqueIdentifier: String = "") -> WakuNetworkRelayer { let logger = ConsoleLogger() let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket)) - return WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") + return WakuNetworkRelayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: uniqueIdentifier) } func testSubscribe() { @@ -32,8 +32,8 @@ final class RelayerEndToEndTests: XCTestCase { } func testEndToEndPayload() { - let relayA = makeRelayer() - let relayB = makeRelayer() + let relayA = makeRelayer("A") + let relayB = makeRelayer("B") try! relayA.connect() try! relayB.connect() diff --git a/Tests/RelayerTests/WakuRelayTests.swift b/Tests/RelayerTests/WakuRelayTests.swift index 8eca88978..ec3a7e2b4 100644 --- a/Tests/RelayerTests/WakuRelayTests.swift +++ b/Tests/RelayerTests/WakuRelayTests.swift @@ -6,13 +6,13 @@ import XCTest @testable import Relayer class WakuRelayTests: XCTestCase { - var wakuRelay: WakuNetworkRelay! + var wakuRelay: WakuNetworkRelayer! var dispatcher: DispatcherMock! override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - wakuRelay = WakuNetworkRelay(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") + wakuRelay = WakuNetworkRelayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") } override func tearDown() { From e6d0bf817a644ff9e75f32c869a178c86423e51d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 10:23:11 +0100 Subject: [PATCH 20/28] update initialisers, add docs --- Sources/Relayer/WakuNetworkRelayer.swift | 20 ++++++++++++----- .../WalletConnect/WalletConnectClient.swift | 22 +++++++++++++------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Sources/Relayer/WakuNetworkRelayer.swift b/Sources/Relayer/WakuNetworkRelayer.swift index 2258a91bc..1348c0283 100644 --- a/Sources/Relayer/WakuNetworkRelayer.swift +++ b/Sources/Relayer/WakuNetworkRelayer.swift @@ -28,7 +28,7 @@ public final class WakuNetworkRelayer { requestAcknowledgePublisherSubject.eraseToAnyPublisher() } private let requestAcknowledgePublisherSubject = PassthroughSubject, Never>() - private let logger: ConsoleLogging + let logger: ConsoleLogging init(dispatcher: Dispatching, logger: ConsoleLogging, @@ -41,13 +41,23 @@ public final class WakuNetworkRelayer { setUpBindings() } - public convenience init(logger: ConsoleLogging, - url: URL, + /// Instantiates Relayer + /// - Parameters: + /// - relayHost: proxy server host that your application will use to connect to Waku Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` + /// - projectId: an optional parameter used to access the public WalletConnect infrastructure. Go to `www.walletconnect.com` for info. + /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults + /// - uniqueIdentifier: if your app requires more than one relayer instances you are required to call identify them + /// - socketConnectionType: socket connection type + /// - logger: logger instance + public convenience init(relayHost: String, + projectId: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, uniqueIdentifier: String? = nil, - socketConnectionType: SocketConnectionType = .automatic) { + socketConnectionType: SocketConnectionType = .automatic, + logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off)) { let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) + let url = Self.makeRelayUrl(host: relayHost, projectId: projectId) let socket = WebSocketSession(session: urlSession, url: url) var socketConnectionHandler: SocketConnectionHandler switch socketConnectionType { @@ -200,7 +210,7 @@ public final class WakuNetworkRelayer { } } - static public func makeRelayUrl(host: String, projectId: String) -> URL { + static private func makeRelayUrl(host: String, projectId: String) -> URL { var components = URLComponents() components.scheme = "wss" components.host = host diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index f8abd0ec8..fb54d5006 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -40,7 +40,7 @@ public final class WalletConnectClient { /// - projectId: an optional parameter used to access the public WalletConnect infrastructure. Go to `www.walletconnect.com` for info. /// - isController: the peer that controls communication permissions for allowed chains, notification types and JSON-RPC request methods. Always true for a wallet. /// - relayHost: proxy server host that your application will use to connect to Waku Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` - /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults but if for some reasons you want to provide your own storage you can inject it here. + /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. @@ -55,8 +55,7 @@ public final class WalletConnectClient { // try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) - let relayUrl = WakuNetworkRelayer.makeRelayUrl(host: relayHost, projectId: projectId) - let relayer = WakuNetworkRelayer(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") + let relayer = WakuNetworkRelayer(relayHost: relayHost, projectId: projectId, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "", logger: logger) let serializer = JSONRPCSerializer(crypto: crypto) self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) @@ -68,11 +67,21 @@ public final class WalletConnectClient { setUpEnginesCallbacks() } - public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayer: WakuNetworkRelayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { - self.init(metadata: metadata, projectId: projectId, isController: isController, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) + /// Initializes and returns newly created WalletConnect Client Instance. Establishes a network connection with the relay + /// + /// - Parameters: + /// - metadata: describes your application and will define pairing appearance in a web browser. + /// - isController: the peer that controls communication permissions for allowed chains, notification types and JSON-RPC request methods. Always true for a wallet. + /// - relayer: Relayer instance + /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults but if for some reasons you want to provide your own storage you can inject it here. + /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. + /// + /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. + public convenience init(metadata: AppMetadata, isController: Bool, relayer: WakuNetworkRelayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + self.init(metadata: metadata, isController: isController, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, projectId: String, isController: Bool, relayer: WakuNetworkRelayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, isController: Bool, relayer: WakuNetworkRelayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata self.isController = isController self.logger = logger @@ -85,7 +94,6 @@ public final class WalletConnectClient { let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) - self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) setUpEnginesCallbacks() } From ada029ab38dbdfce6c4f5a254c3b411a2b6ee6d4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 10:33:44 +0100 Subject: [PATCH 21/28] rename Relayer --- Example/DApp/ClientDelegate.swift | 2 ++ Sources/Relayer/{WakuNetworkRelayer.swift => Relayer.swift} | 2 +- Sources/WalletConnect/Relay/NetworkRelaying.swift | 2 +- Sources/WalletConnect/WalletConnectClient.swift | 6 +++--- Tests/RelayerTests/RelayerEndToEndTests.swift | 4 ++-- Tests/RelayerTests/WakuRelayTests.swift | 4 ++-- 6 files changed, 11 insertions(+), 9 deletions(-) rename Sources/Relayer/{WakuNetworkRelayer.swift => Relayer.swift} (99%) diff --git a/Example/DApp/ClientDelegate.swift b/Example/DApp/ClientDelegate.swift index 2fecc1d15..1e52a8b4c 100644 --- a/Example/DApp/ClientDelegate.swift +++ b/Example/DApp/ClientDelegate.swift @@ -1,4 +1,5 @@ import WalletConnect +import Relayer class ClientDelegate: WalletConnectClientDelegate { var client: WalletConnectClient @@ -13,6 +14,7 @@ class ClientDelegate: WalletConnectClientDelegate { description: "a description", url: "wallet.connect", icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) + let relayer = Relayer() self.client = WalletConnectClient( metadata: metadata, projectId: "52af113ee0c1e1a20f4995730196c13e", diff --git a/Sources/Relayer/WakuNetworkRelayer.swift b/Sources/Relayer/Relayer.swift similarity index 99% rename from Sources/Relayer/WakuNetworkRelayer.swift rename to Sources/Relayer/Relayer.swift index 1348c0283..a1dc3fb94 100644 --- a/Sources/Relayer/WakuNetworkRelayer.swift +++ b/Sources/Relayer/Relayer.swift @@ -4,7 +4,7 @@ import Combine import WalletConnectUtils -public final class WakuNetworkRelayer { +public final class Relayer { enum RelyerError: Error { case subscriptionIdNotFound } diff --git a/Sources/WalletConnect/Relay/NetworkRelaying.swift b/Sources/WalletConnect/Relay/NetworkRelaying.swift index fa1340f67..93f9a2b9b 100644 --- a/Sources/WalletConnect/Relay/NetworkRelaying.swift +++ b/Sources/WalletConnect/Relay/NetworkRelaying.swift @@ -2,7 +2,7 @@ import Foundation import Relayer -extension WakuNetworkRelayer: NetworkRelaying {} +extension Relayer: NetworkRelaying {} protocol NetworkRelaying { var onConnect: (()->())? {get set} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index fb54d5006..edfb7bc57 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -55,7 +55,7 @@ public final class WalletConnectClient { // try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) - let relayer = WakuNetworkRelayer(relayHost: relayHost, projectId: projectId, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "", logger: logger) + let relayer = Relayer(relayHost: relayHost, projectId: projectId, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "", logger: logger) let serializer = JSONRPCSerializer(crypto: crypto) self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) @@ -77,11 +77,11 @@ public final class WalletConnectClient { /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public convenience init(metadata: AppMetadata, isController: Bool, relayer: WakuNetworkRelayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + public convenience init(metadata: AppMetadata, isController: Bool, relayer: Relayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { self.init(metadata: metadata, isController: isController, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, isController: Bool, relayer: WakuNetworkRelayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, isController: Bool, relayer: Relayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata self.isController = isController self.logger = logger diff --git a/Tests/RelayerTests/RelayerEndToEndTests.swift b/Tests/RelayerTests/RelayerEndToEndTests.swift index 8bbe0373f..e87da3611 100644 --- a/Tests/RelayerTests/RelayerEndToEndTests.swift +++ b/Tests/RelayerTests/RelayerEndToEndTests.swift @@ -11,13 +11,13 @@ final class RelayerEndToEndTests: XCTestCase { let url = URL(string: "wss://staging.walletconnect.org")! private var publishers = [AnyCancellable]() - func makeRelayer(_ uniqueIdentifier: String = "") -> WakuNetworkRelayer { + func makeRelayer(_ uniqueIdentifier: String = "") -> Relayer { let logger = ConsoleLogger() let socketConnectionObserver = SocketConnectionObserver() let urlSession = URLSession(configuration: .default, delegate: socketConnectionObserver, delegateQueue: OperationQueue()) let socket = WebSocketSession(session: urlSession, url: url) let dispatcher = Dispatcher(socket: socket, socketConnectionObserver: socketConnectionObserver, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket)) - return WakuNetworkRelayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: uniqueIdentifier) + return Relayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: uniqueIdentifier) } func testSubscribe() { diff --git a/Tests/RelayerTests/WakuRelayTests.swift b/Tests/RelayerTests/WakuRelayTests.swift index ec3a7e2b4..9387ccda5 100644 --- a/Tests/RelayerTests/WakuRelayTests.swift +++ b/Tests/RelayerTests/WakuRelayTests.swift @@ -6,13 +6,13 @@ import XCTest @testable import Relayer class WakuRelayTests: XCTestCase { - var wakuRelay: WakuNetworkRelayer! + var wakuRelay: Relayer! var dispatcher: DispatcherMock! override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - wakuRelay = WakuNetworkRelayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") + wakuRelay = Relayer(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), uniqueIdentifier: "") } override func tearDown() { From 1e5219426ab0e30b99fe139057d5ef3c458dbfd8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 10:35:52 +0100 Subject: [PATCH 22/28] add example with injectable relayer in dapp --- Example/DApp/ClientDelegate.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Example/DApp/ClientDelegate.swift b/Example/DApp/ClientDelegate.swift index 1e52a8b4c..67e410d19 100644 --- a/Example/DApp/ClientDelegate.swift +++ b/Example/DApp/ClientDelegate.swift @@ -14,13 +14,8 @@ class ClientDelegate: WalletConnectClientDelegate { description: "a description", url: "wallet.connect", icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) - let relayer = Relayer() - self.client = WalletConnectClient( - metadata: metadata, - projectId: "52af113ee0c1e1a20f4995730196c13e", - isController: false, - relayHost: "relay.dev.walletconnect.com" - ) + let relayer = Relayer(relayHost: "relay.dev.walletconnect.com", projectId: "52af113ee0c1e1a20f4995730196c13e") + self.client = WalletConnectClient(metadata: metadata, isController: false, relayer: relayer) client.delegate = self } From 969b9198c6bb799f06fde0933e39dae9be974bbb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 14:55:57 +0100 Subject: [PATCH 23/28] remove controller flag, fix tests --- .../WalletConnect/Engine/PairingEngine.swift | 7 +- .../WalletConnect/Engine/SessionEngine.swift | 28 ++--- .../WalletConnect/WalletConnectClient.swift | 21 ++-- Tests/IntegrationTests/ClientTest.swift | 1 - .../PairingEngineTests.swift | 1 - .../SessionEngineTests.swift | 106 +++++++----------- .../Stub/Session+Stub.swift | 7 +- 7 files changed, 66 insertions(+), 105 deletions(-) diff --git a/Sources/WalletConnect/Engine/PairingEngine.swift b/Sources/WalletConnect/Engine/PairingEngine.swift index 29736ef5a..41e364c9c 100644 --- a/Sources/WalletConnect/Engine/PairingEngine.swift +++ b/Sources/WalletConnect/Engine/PairingEngine.swift @@ -13,7 +13,6 @@ final class PairingEngine { private let wcSubscriber: WCSubscribing private let relayer: WalletConnectRelaying private let crypto: CryptoStorageProtocol - private let isController: Bool private let sequencesStore: PairingSequenceStorage private var appMetadata: AppMetadata private var publishers = [AnyCancellable]() @@ -25,7 +24,6 @@ final class PairingEngine { crypto: CryptoStorageProtocol, subscriber: WCSubscribing, sequencesStore: PairingSequenceStorage, - isController: Bool, metadata: AppMetadata, logger: ConsoleLogging, topicGenerator: @escaping () -> String? = String.generateTopic) { @@ -34,7 +32,6 @@ final class PairingEngine { self.wcSubscriber = subscriber self.appMetadata = metadata self.sequencesStore = sequencesStore - self.isController = isController self.logger = logger self.topicInitializer = topicGenerator setUpWCRequestHandling() @@ -71,7 +68,7 @@ final class PairingEngine { let publicKey = try! crypto.createX25519KeyPair() let relay = RelayProtocolOptions(protocol: "waku", params: nil) - let uri = WalletConnectURI(topic: topic, publicKey: publicKey.hexRepresentation, isController: isController, relay: relay) + let uri = WalletConnectURI(topic: topic, publicKey: publicKey.hexRepresentation, isController: false, relay: relay) let pendingPairing = PairingSequence.buildProposed(uri: uri) sequencesStore.setSequence(pendingPairing) @@ -82,7 +79,7 @@ final class PairingEngine { func approve(_ pairingURI: WalletConnectURI) throws { let proposal = PairingProposal.createFromURI(pairingURI) - guard proposal.proposer.controller != isController else { + guard !proposal.proposer.controller else { throw WalletConnectError.internal(.unauthorizedMatchingController) } guard !hasPairing(for: proposal.topic) else { diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 529fc5810..e9fa175fa 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -17,7 +17,6 @@ final class SessionEngine { private let wcSubscriber: WCSubscribing private let relayer: WalletConnectRelaying private let crypto: CryptoStorageProtocol - private var isController: Bool private var metadata: AppMetadata private var publishers = [AnyCancellable]() private let logger: ConsoleLogging @@ -27,7 +26,6 @@ final class SessionEngine { crypto: CryptoStorageProtocol, subscriber: WCSubscribing, sequencesStore: SessionSequenceStorage, - isController: Bool, metadata: AppMetadata, logger: ConsoleLogging, topicGenerator: @escaping () -> String? = String.generateTopic) { @@ -36,7 +34,6 @@ final class SessionEngine { self.metadata = metadata self.wcSubscriber = subscriber self.sequencesStore = sequencesStore - self.isController = isController self.logger = logger self.topicInitializer = topicGenerator setUpWCRequestHandling() @@ -72,7 +69,7 @@ final class SessionEngine { let proposal = SessionProposal( topic: pendingSessionTopic, relay: relay, - proposer: SessionType.Proposer(publicKey: publicKey.hexRepresentation, controller: isController, metadata: metadata), + proposer: SessionType.Proposer(publicKey: publicKey.hexRepresentation, controller: false, metadata: metadata), signal: SessionType.Signal(method: "pairing", params: SessionType.Signal.Params(topic: settledPairing.topic)), permissions: permissions, ttl: SessionSequence.timeToLivePending) @@ -205,7 +202,7 @@ final class SessionEngine { throw WalletConnectError.internal(.notApproved) // TODO: Use a suitable error cases } } - if !isController || session.settled?.status != .acknowledged { + if !session.isController || session.settled?.status != .acknowledged { throw WalletConnectError.unauthrorized(.unauthorizedUpdateRequest) } session.update(accounts) @@ -221,7 +218,7 @@ final class SessionEngine { guard session.isSettled else { throw WalletConnectError.sessionNotSettled(topic) } - guard isController else { + guard session.isController else { throw WalletConnectError.unauthorizedNonControllerCall } guard validatePermissions(permissions) else { @@ -333,10 +330,6 @@ final class SessionEngine { relayer.respondError(for: payload, reason: .unauthorizedUpdateRequest(context: .session)) return } - guard !isController else { - relayer.respondError(for: payload, reason: .unauthorizedMatchingController(isController: isController)) - return - } session.settled?.state = updateParams.state sequencesStore.setSequence(session) relayer.respondSuccess(for: payload) @@ -356,10 +349,6 @@ final class SessionEngine { relayer.respondError(for: payload, reason: .unauthorizedUpgradeRequest(context: .session)) return } - guard !isController else { - relayer.respondError(for: payload, reason: .unauthorizedMatchingController(isController: isController)) - return - } session.upgrade(upgradeParams.permissions) sequencesStore.setSequence(session) let newPermissions = session.settled!.permissions // We know session is settled @@ -439,16 +428,17 @@ final class SessionEngine { private func handleSessionApprove(_ approveParams: SessionType.ApproveParams, topic: String, requestId: Int64) { logger.debug("Responder Client approved session on topic: \(topic)") - logger.debug("isController: \(isController)") - guard !isController else { - logger.warn("Warning: Session Engine - Unexpected handleSessionApprove method call by non Controller client") - return - } guard let session = sequencesStore.getSequence(forTopic: topic), let pendingSession = session.pending else { logger.error("Could not find pending session for topic: \(topic)") return } + logger.debug("isController: \(session.isController)") + + guard !session.isController else { + logger.warn("Warning: Session Engine - Unexpected handleSessionApprove method call by non Controller client") + return + } logger.debug("handleSessionApprove") let agreementKeys = try! crypto.performKeyAgreement(selfPublicKey: try! session.getPublicKey(), peerPublicKey: approveParams.responder.publicKey) diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index edfb7bc57..fef460286 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -22,7 +22,6 @@ public final class WalletConnectClient { public weak var delegate: WalletConnectClientDelegate? public let logger: ConsoleLogging private let metadata: AppMetadata - private let isController: Bool private let pairingEngine: PairingEngine private let sessionEngine: SessionEngine private let relay: WalletConnectRelaying @@ -44,13 +43,12 @@ public final class WalletConnectClient { /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { - self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) + public convenience init(metadata: AppMetadata, projectId: String, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + self.init(metadata: metadata, projectId: projectId, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, projectId: String, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata - self.isController = isController self.logger = logger // try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever self.crypto = Crypto(keychain: keychain) @@ -61,9 +59,9 @@ public final class WalletConnectClient { self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) - self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) + self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, metadata: metadata, logger: logger) - self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) + self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, metadata: metadata, logger: logger) setUpEnginesCallbacks() } @@ -78,12 +76,11 @@ public final class WalletConnectClient { /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. public convenience init(metadata: AppMetadata, isController: Bool, relayer: Relayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { - self.init(metadata: metadata, isController: isController, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) + self.init(metadata: metadata, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, isController: Bool, relayer: Relayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, relayer: Relayer, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata - self.isController = isController self.logger = logger // try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever self.crypto = Crypto(keychain: keychain) @@ -93,8 +90,8 @@ public final class WalletConnectClient { self.relay = WalletConnectRelay(networkRelayer: relayer, jsonRpcSerializer: serializer, logger: logger, jsonRpcHistory: history) let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) - self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) - self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) + self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, metadata: metadata, logger: logger) + self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, metadata: metadata, logger: logger) setUpEnginesCallbacks() } diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index ecf9a5a83..39a6e47d5 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -30,7 +30,6 @@ final class ClientTests: XCTestCase { let client = WalletConnectClient( metadata: AppMetadata(name: nil, description: nil, url: nil, icons: nil), projectId: projectId, - isController: isController, relayHost: relayHost, logger: logger, keychain: KeychainStorage(keychainService: KeychainServiceFake()), diff --git a/Tests/WalletConnectTests/PairingEngineTests.swift b/Tests/WalletConnectTests/PairingEngineTests.swift index 6f0369b2f..9223b182b 100644 --- a/Tests/WalletConnectTests/PairingEngineTests.swift +++ b/Tests/WalletConnectTests/PairingEngineTests.swift @@ -43,7 +43,6 @@ final class PairingEngineTests: XCTestCase { crypto: cryptoMock, subscriber: subscriberMock, sequencesStore: storageMock, - isController: isController, metadata: meta, logger: logger, topicGenerator: topicGenerator.getTopic) diff --git a/Tests/WalletConnectTests/SessionEngineTests.swift b/Tests/WalletConnectTests/SessionEngineTests.swift index 9c480ad1a..eb9389bfb 100644 --- a/Tests/WalletConnectTests/SessionEngineTests.swift +++ b/Tests/WalletConnectTests/SessionEngineTests.swift @@ -14,7 +14,6 @@ final class SessionEngineTests: XCTestCase { var topicGenerator: TopicGenerator! - var isController: Bool! var metadata: AppMetadata! override func setUp() { @@ -34,23 +33,21 @@ final class SessionEngineTests: XCTestCase { engine = nil } - func setupEngine(isController: Bool) { + func setupEngine() { metadata = AppMetadata(name: nil, description: nil, url: nil, icons: nil) - self.isController = isController let logger = ConsoleLoggerMock() engine = SessionEngine( relay: relayMock, crypto: cryptoMock, subscriber: subscriberMock, sequencesStore: storageMock, - isController: isController, metadata: metadata, logger: logger, topicGenerator: topicGenerator.getTopic) } func testPropose() { - setupEngine(isController: false) + setupEngine() let pairing = Pairing.stub() @@ -77,7 +74,7 @@ final class SessionEngineTests: XCTestCase { } func testProposeResponseFailure() { - setupEngine(isController: false) + setupEngine() let pairing = Pairing.stub() let topicB = pairing.topic @@ -108,13 +105,13 @@ final class SessionEngineTests: XCTestCase { } func testApprove() { - setupEngine(isController: true) + setupEngine() let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let topicB = String.generateTopic()! let topicC = String.generateTopic()! let topicD = deriveTopic(publicKey: proposerPubKey, privateKey: cryptoMock.privateKeyStub) - let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: isController, metadata: metadata) + let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: true, metadata: metadata) let proposal = SessionProposal( topic: topicC, relay: RelayProtocolOptions(protocol: "", params: nil), @@ -139,7 +136,7 @@ final class SessionEngineTests: XCTestCase { } func testApprovalAcknowledgementSuccess() { - setupEngine(isController: true) + setupEngine() let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let topicB = String.generateTopic()! @@ -149,7 +146,7 @@ final class SessionEngineTests: XCTestCase { let agreementKeys = AgreementSecret.stub() cryptoMock.setAgreementSecret(agreementKeys, topic: topicC) - let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: isController, metadata: metadata) + let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: true, metadata: metadata) let proposal = SessionProposal( topic: topicC, relay: RelayProtocolOptions(protocol: "", params: nil), @@ -178,7 +175,7 @@ final class SessionEngineTests: XCTestCase { } func testApprovalAcknowledgementFailure() { - setupEngine(isController: true) + setupEngine() let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let selfPubKey = cryptoMock.privateKeyStub.publicKey.hexRepresentation @@ -189,7 +186,7 @@ final class SessionEngineTests: XCTestCase { let agreementKeys = AgreementSecret.stub() cryptoMock.setAgreementSecret(agreementKeys, topic: topicC) - let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: isController, metadata: metadata) + let proposer = SessionType.Proposer(publicKey: proposerPubKey, controller: true, metadata: metadata) let proposal = SessionProposal( topic: topicC, relay: RelayProtocolOptions(protocol: "", params: nil), @@ -223,7 +220,7 @@ final class SessionEngineTests: XCTestCase { } func testReceiveApprovalResponse() { - setupEngine(isController: false) + setupEngine() var approvedSession: Session? @@ -265,34 +262,34 @@ final class SessionEngineTests: XCTestCase { // MARK: - Update call tests func testUpdateSuccess() throws { - setupEngine(isController: true) - let session = SessionSequence.stubSettled() + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: true) storageMock.setSequence(session) try engine.update(topic: session.topic, accounts: ["std:0:0"]) XCTAssertTrue(relayMock.didCallRequest) } func testUpdateErrorInvalidAccount() { - setupEngine(isController: true) - let session = SessionSequence.stubSettled() + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: true) storageMock.setSequence(session) XCTAssertThrowsError(try engine.update(topic: session.topic, accounts: ["err"])) } func testUpdateErrorIfNonController() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled() + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) XCTAssertThrowsError(try engine.update(topic: session.topic, accounts: ["std:0:0"]), "Update must fail if called by a non-controller.") } func testUpdateErrorSessionNotFound() { - setupEngine(isController: true) + setupEngine() XCTAssertThrowsError(try engine.update(topic: "", accounts: ["std:0:0"]), "Update must fail if there is no session matching the target topic.") } func testUpdateErrorSessionNotSettled() { - setupEngine(isController: true) + setupEngine() let session = SessionSequence.stubPreSettled() storageMock.setSequence(session) XCTAssertThrowsError(try engine.update(topic: session.topic, accounts: ["std:0:0"]), "Update must fail if session is not on settled state.") @@ -301,16 +298,16 @@ final class SessionEngineTests: XCTestCase { // MARK: - Update peer response tests func testUpdatePeerSuccess() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled(isPeerController: true) + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: session.topic)) XCTAssertTrue(relayMock.didRespondSuccess) } func testUpdatePeerErrorAccountInvalid() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled(isPeerController: true) + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: session.topic, accounts: ["0"])) XCTAssertFalse(relayMock.didRespondSuccess) @@ -318,14 +315,14 @@ final class SessionEngineTests: XCTestCase { } func testUpdatePeerErrorNoSession() { - setupEngine(isController: false) + setupEngine() subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: "")) XCTAssertFalse(relayMock.didRespondSuccess) XCTAssertEqual(relayMock.lastErrorCode, 1301) } func testUpdatePeerErrorSessionNotSettled() { - setupEngine(isController: false) + setupEngine() let session = SessionSequence.stubPreSettled(isPeerController: true) // Session is not fully settled storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: session.topic)) @@ -334,31 +331,21 @@ final class SessionEngineTests: XCTestCase { } func testUpdatePeerErrorUnauthorized() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled() // Peer is not a controller + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: true) // Peer is not a controller storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: session.topic)) XCTAssertFalse(relayMock.didRespondSuccess) XCTAssertEqual(relayMock.lastErrorCode, 3003) } - - func testUpdatePeerErrorMatchingController() { - setupEngine(isController: true) // Update request received by a controller - let session = SessionSequence.stubSettled(isPeerController: true) - storageMock.setSequence(session) - subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpdate(topic: session.topic)) - XCTAssertFalse(relayMock.didRespondSuccess) - XCTAssertEqual(relayMock.lastErrorCode, 3005) - } - // TODO: Update acknowledgement tests // MARK: - Upgrade call tests func testUpgradeSuccess() throws { - setupEngine(isController: true) + setupEngine() let permissions = Session.Permissions.stub() - let session = SessionSequence.stubSettled() + let session = SessionSequence.stubSettled(isSelfController: true) storageMock.setSequence(session) try engine.upgrade(topic: session.topic, permissions: permissions) XCTAssertTrue(relayMock.didCallRequest) @@ -366,14 +353,14 @@ final class SessionEngineTests: XCTestCase { } func testUpgradeErrorSessionNotFound() { - setupEngine(isController: true) + setupEngine() XCTAssertThrowsError(try engine.upgrade(topic: "", permissions: Session.Permissions.stub())) { error in XCTAssertTrue(error.isNoSessionMatchingTopicError) } } func testUpgradeErrorSessionNotSettled() { - setupEngine(isController: true) + setupEngine() let session = SessionSequence.stubPreSettled() storageMock.setSequence(session) XCTAssertThrowsError(try engine.upgrade(topic: session.topic, permissions: Session.Permissions.stub())) { error in @@ -382,8 +369,8 @@ final class SessionEngineTests: XCTestCase { } func testUpgradeErrorInvalidPermissions() { - setupEngine(isController: true) - let session = SessionSequence.stubSettled() + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: true) storageMock.setSequence(session) XCTAssertThrowsError(try engine.upgrade(topic: session.topic, permissions: Session.Permissions.stub(chains: [""]))) { error in XCTAssertTrue(error.isInvalidPermissionsError) @@ -397,8 +384,8 @@ final class SessionEngineTests: XCTestCase { } func testUpgradeErrorCalledByNonController() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled() + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) XCTAssertThrowsError(try engine.upgrade(topic: session.topic, permissions: Session.Permissions.stub())) { error in XCTAssertTrue(error.isUnauthorizedNonControllerCallError) @@ -408,9 +395,9 @@ final class SessionEngineTests: XCTestCase { // MARK: - Upgrade peer response tests func testUpgradePeerSuccess() { - setupEngine(isController: false) + setupEngine() var didCallbackUpgrade = false - let session = SessionSequence.stubSettled(isPeerController: true) + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) engine.onSessionUpgrade = { topic, _ in didCallbackUpgrade = true @@ -422,9 +409,9 @@ final class SessionEngineTests: XCTestCase { } func testUpgradePeerErrorInvalidPermissions() { - setupEngine(isController: false) + setupEngine() let invalidPermissions = SessionPermissions.stub(chains: [""]) - let session = SessionSequence.stubSettled(isPeerController: true) + let session = SessionSequence.stubSettled(isSelfController: false) storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpgrade(topic: session.topic, permissions: invalidPermissions)) XCTAssertFalse(relayMock.didRespondSuccess) @@ -432,14 +419,14 @@ final class SessionEngineTests: XCTestCase { } func testUpgradePeerErrorSessionNotFound() { - setupEngine(isController: false) + setupEngine() subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpgrade(topic: "")) XCTAssertFalse(relayMock.didRespondSuccess) XCTAssertEqual(relayMock.lastErrorCode, 1301) } func testUpgradePeerErrorSessionNotSettled() { - setupEngine(isController: false) + setupEngine() let session = SessionSequence.stubPreSettled(isPeerController: true) // Session is not fully settled storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpgrade(topic: session.topic)) @@ -448,22 +435,13 @@ final class SessionEngineTests: XCTestCase { } func testUpgradePeerErrorUnauthorized() { - setupEngine(isController: false) - let session = SessionSequence.stubSettled() // Peer is not a controller + setupEngine() + let session = SessionSequence.stubSettled(isSelfController: true) // Peer is not a controller storageMock.setSequence(session) subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpgrade(topic: session.topic)) XCTAssertFalse(relayMock.didRespondSuccess) XCTAssertEqual(relayMock.lastErrorCode, 3004) } - func testUpgradePeerErrorMatchingController() { - setupEngine(isController: true) // Upgrade request received by a controller - let session = SessionSequence.stubSettled(isPeerController: true) - storageMock.setSequence(session) - subscriberMock.onReceivePayload?(WCRequestSubscriptionPayload.stubUpgrade(topic: session.topic)) - XCTAssertFalse(relayMock.didRespondSuccess) - XCTAssertEqual(relayMock.lastErrorCode, 3005) - } - // TODO: Upgrade acknowledgement tests } diff --git a/Tests/WalletConnectTests/Stub/Session+Stub.swift b/Tests/WalletConnectTests/Stub/Session+Stub.swift index cc67dacf8..a711a2acf 100644 --- a/Tests/WalletConnectTests/Stub/Session+Stub.swift +++ b/Tests/WalletConnectTests/Stub/Session+Stub.swift @@ -20,13 +20,14 @@ extension SessionSequence { ) } - static func stubSettled(isPeerController: Bool = false) -> SessionSequence { + static func stubSettled(isSelfController: Bool) -> SessionSequence { let peerKey = AgreementPrivateKey().publicKey.hexRepresentation - let permissions = isPeerController ? SessionPermissions.stub(controllerKey: peerKey) : SessionPermissions.stub() + let selfKey = AgreementPrivateKey().publicKey.hexRepresentation + let permissions = isSelfController ? SessionPermissions.stub(controllerKey: selfKey) : SessionPermissions.stub(controllerKey: peerKey) return SessionSequence( topic: String.generateTopic()!, relay: RelayProtocolOptions.stub(), - selfParticipant: Participant.stub(), + selfParticipant: Participant.stub(publicKey: selfKey), expiryDate: Date.distantFuture, settledState: Settled( peer: Participant.stub(publicKey: peerKey), From 32285a1d2a82694c9f2b8b39f4fcc76b21727635 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 8 Feb 2022 15:48:16 +0100 Subject: [PATCH 24/28] remove controller flag in remaining places --- Example/DApp/ClientDelegate.swift | 2 +- Example/ExampleApp/Responder/ResponderViewController.swift | 1 - README.md | 2 -- Sources/WalletConnect/WalletConnectClient.swift | 6 ++---- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Example/DApp/ClientDelegate.swift b/Example/DApp/ClientDelegate.swift index 67e410d19..b958fd659 100644 --- a/Example/DApp/ClientDelegate.swift +++ b/Example/DApp/ClientDelegate.swift @@ -15,7 +15,7 @@ class ClientDelegate: WalletConnectClientDelegate { url: "wallet.connect", icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) let relayer = Relayer(relayHost: "relay.dev.walletconnect.com", projectId: "52af113ee0c1e1a20f4995730196c13e") - self.client = WalletConnectClient(metadata: metadata, isController: false, relayer: relayer) + self.client = WalletConnectClient(metadata: metadata, relayer: relayer) client.delegate = self } diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index c5e662ca6..2555ccd3f 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -15,7 +15,6 @@ final class ResponderViewController: UIViewController { return WalletConnectClient( metadata: metadata, projectId: "52af113ee0c1e1a20f4995730196c13e", - isController: true, relayHost: "relay.dev.walletconnect.com" ) }() diff --git a/README.md b/README.md index 0da9866f2..b2ffdd919 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,8 @@ You usually want to have a single instance of a client in you app. icons: [String]?) let client = WalletConnectClient(metadata: AppMetadata, projectId: String, - isController: Bool, relayHost: String) ``` -The `controller` client will always be the "wallet" which is exposing blockchain accounts to a "dapp" and therefore is also in charge of signing. After instantiation of a client set its delegate. #### Pair Clients diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index fef460286..d8f64b1ea 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -12,7 +12,7 @@ import UIKit /// /// ```swift /// let metadata = AppMetadata(name: String?, description: String?, url: String?, icons: [String]?) -/// let client = WalletConnectClient(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String) +/// let client = WalletConnectClient(metadata: AppMetadata, projectId: String, relayHost: String) /// ``` /// /// - Parameters: @@ -37,7 +37,6 @@ public final class WalletConnectClient { /// - Parameters: /// - metadata: describes your application and will define pairing appearance in a web browser. /// - projectId: an optional parameter used to access the public WalletConnect infrastructure. Go to `www.walletconnect.com` for info. - /// - isController: the peer that controls communication permissions for allowed chains, notification types and JSON-RPC request methods. Always true for a wallet. /// - relayHost: proxy server host that your application will use to connect to Waku Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. @@ -69,13 +68,12 @@ public final class WalletConnectClient { /// /// - Parameters: /// - metadata: describes your application and will define pairing appearance in a web browser. - /// - isController: the peer that controls communication permissions for allowed chains, notification types and JSON-RPC request methods. Always true for a wallet. /// - relayer: Relayer instance /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults but if for some reasons you want to provide your own storage you can inject it here. /// - clientName: if your app requires more than one client you are required to call them with different names to distinguish logs source and prefix storage keys. /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public convenience init(metadata: AppMetadata, isController: Bool, relayer: Relayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { + public convenience init(metadata: AppMetadata, relayer: Relayer, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { self.init(metadata: metadata, relayer: relayer, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } From 72d395e32252eec84a474140176edf83662c4925 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 9 Feb 2022 10:40:57 +0100 Subject: [PATCH 25/28] Restrict rejection reason to possible CAIP-25 codes --- .../Responder/ResponderViewController.swift | 2 +- .../WalletConnect/Engine/SessionEngine.swift | 4 ++-- Sources/WalletConnect/RejectionReason.swift | 22 +++++++++++++++++++ .../WalletConnect/WalletConnectClient.swift | 6 ++--- Tests/IntegrationTests/ClientTest.swift | 2 +- 5 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 Sources/WalletConnect/RejectionReason.swift diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index 2555ccd3f..056bd298c 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -158,7 +158,7 @@ extension ResponderViewController: SessionViewControllerDelegate { print("did reject session") let proposal = currentProposal! currentProposal = nil - client.reject(proposal: proposal, reason: Reason(code: 0, message: "reject")) + client.reject(proposal: proposal, reason: .disapprovedChains) } } diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index e9fa175fa..3fa684863 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -129,8 +129,8 @@ final class SessionEngine { } } - func reject(proposal: SessionProposal, reason: Reason) { - let rejectParams = SessionType.RejectParams(reason: reason.toInternal()) + func reject(proposal: SessionProposal, reason: SessionType.Reason ) { + let rejectParams = SessionType.RejectParams(reason: reason) relayer.request(.wcSessionReject(rejectParams), onTopic: proposal.topic) { [weak self] result in self?.logger.debug("Reject result: \(result)") } diff --git a/Sources/WalletConnect/RejectionReason.swift b/Sources/WalletConnect/RejectionReason.swift new file mode 100644 index 000000000..5b43588f2 --- /dev/null +++ b/Sources/WalletConnect/RejectionReason.swift @@ -0,0 +1,22 @@ + +import Foundation + +/// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md +public enum RejectionReason { + case disapprovedChains + case disapprovedMethods + case disapprovedNotificationTypes +} + +internal extension RejectionReason { + func internalRepresentation() -> SessionType.Reason { + switch self { + case .disapprovedChains: + return SessionType.Reason(code: 5000, message: "User disapproved requested chains") + case .disapprovedMethods: + return SessionType.Reason(code: 5001, message: "User disapproved requested json-rpc methods") + case .disapprovedNotificationTypes: + return SessionType.Reason(code: 5002, message: "User disapproved requested notification types") + } + } +} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index d8f64b1ea..bc45a407e 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -146,9 +146,9 @@ public final class WalletConnectClient { /// For the responder to reject a session proposal. /// - Parameters: /// - proposal: Session Proposal received from peer client in a WalletConnect delegate. - /// - reason: Reason why the session proposal was rejected. - public func reject(proposal: Session.Proposal, reason: Reason) { - sessionEngine.reject(proposal: proposal.proposal, reason: reason) + /// - reason: Reason why the session proposal was rejected. Conforms to CAIP25. + public func reject(proposal: Session.Proposal, reason: RejectionReason) { + sessionEngine.reject(proposal: proposal.proposal, reason: reason.internalRepresentation()) } /// For the responder to update the accounts diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index 39a6e47d5..99dba3156 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -112,7 +112,7 @@ final class ClientTests: XCTestCase { let uri = try! proposer.client.connect(sessionPermissions: permissions)! _ = try! responder.client.pair(uri: uri) responder.onSessionProposal = {[unowned self] proposal in - self.responder.client.reject(proposal: proposal, reason: Reason(code: WalletConnectError.internal(.notApproved).code, message: WalletConnectError.internal(.notApproved).description)) + self.responder.client.reject(proposal: proposal, reason: .disapprovedChains) } proposer.onSessionRejected = { _, reason in XCTAssertEqual(reason.code, WalletConnectError.internal(.notApproved).code) From 7ff956103a9a9fe0c84e7d2a3c0ee20dd2bcef7d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 9 Feb 2022 10:56:50 +0100 Subject: [PATCH 26/28] move Data+Hex to utils --- Sources/Relayer/WebSocketSession.swift | 2 +- .../Extensions => WalletConnectUtils}/Data+Hex.swift | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename Sources/{WalletConnect/Extensions => WalletConnectUtils}/Data+Hex.swift (100%) diff --git a/Sources/Relayer/WebSocketSession.swift b/Sources/Relayer/WebSocketSession.swift index 7d1d83688..917cac100 100644 --- a/Sources/Relayer/WebSocketSession.swift +++ b/Sources/Relayer/WebSocketSession.swift @@ -73,7 +73,7 @@ final class WebSocketSession: NSObject, WebSocketSessionProtocol, WebSocketConne case .string(let text): onMessageReceived?(text) case .data(let data): - print("Transport: Unexpected type of message received: \(data)") + print("Transport: Unexpected type of message received: \(data.toHexString)") @unknown default: print("Transport: Unexpected type of message received") } diff --git a/Sources/WalletConnect/Extensions/Data+Hex.swift b/Sources/WalletConnectUtils/Data+Hex.swift similarity index 100% rename from Sources/WalletConnect/Extensions/Data+Hex.swift rename to Sources/WalletConnectUtils/Data+Hex.swift From e17990433462fbfe1a52b98cd550fa4ae387cfa0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 9 Feb 2022 11:00:51 +0100 Subject: [PATCH 27/28] fix - call function --- Sources/Relayer/WebSocketSession.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Relayer/WebSocketSession.swift b/Sources/Relayer/WebSocketSession.swift index 917cac100..b333fb66c 100644 --- a/Sources/Relayer/WebSocketSession.swift +++ b/Sources/Relayer/WebSocketSession.swift @@ -73,7 +73,7 @@ final class WebSocketSession: NSObject, WebSocketSessionProtocol, WebSocketConne case .string(let text): onMessageReceived?(text) case .data(let data): - print("Transport: Unexpected type of message received: \(data.toHexString)") + print("Transport: Unexpected type of message received: \(data.toHexString())") @unknown default: print("Transport: Unexpected type of message received") } From 6569ff4cee0136edb5f2a27d8577f409ea92899a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 9 Feb 2022 11:32:02 +0100 Subject: [PATCH 28/28] fix integration test --- Tests/IntegrationTests/ClientTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index 99dba3156..df2716839 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -115,7 +115,7 @@ final class ClientTests: XCTestCase { self.responder.client.reject(proposal: proposal, reason: .disapprovedChains) } proposer.onSessionRejected = { _, reason in - XCTAssertEqual(reason.code, WalletConnectError.internal(.notApproved).code) + XCTAssertEqual(reason.code, 5000) sessionRejectExpectation.fulfill() } waitForExpectations(timeout: defaultTimeout, handler: nil)