Skip to content

Commit

Permalink
Merge pull request #1349 from WalletConnect/develop
Browse files Browse the repository at this point in the history
1.18.7
  • Loading branch information
llbartekll authored May 3, 2024
2 parents 5305e48 + 4169e4b commit 1fcdbdc
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 71 deletions.
1 change: 1 addition & 0 deletions Example/DApp/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
)

Sign.instance.logger.setLogging(level: .debug)
Networking.instance.setLogging(level: .debug)

Sign.instance.logsPublisher.sink { log in
switch log {
Expand Down
17 changes: 14 additions & 3 deletions Example/RelayIntegrationTests/RelayClientEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,26 @@ final class RelayClientEndToEndTests: XCTestCase {
projectId: InputConfig.projectId,
socketAuthenticator: socketAuthenticator
)
let socket = WebSocket(url: urlFactory.create(fallback: false))
let socket = WebSocket(url: urlFactory.create())
let webSocketFactory = WebSocketFactoryMock(webSocket: socket)
let networkMonitor = NetworkMonitor()

let relayUrlFactory = RelayUrlFactory(
relayHost: "relay.walletconnect.com",
projectId: "1012db890cf3cfb0c1cdc929add657ba",
socketAuthenticator: socketAuthenticator
)

let socketUrlFallbackHandler = SocketUrlFallbackHandler(relayUrlFactory: relayUrlFactory, logger: logger, socket: socket, networkMonitor: networkMonitor)

let socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketUrlFallbackHandler)
let dispatcher = Dispatcher(
socketFactory: webSocketFactory,
relayUrlFactory: urlFactory,
networkMonitor: networkMonitor,
socketConnectionType: .manual,
logger: logger
socket: socket,
logger: logger,
socketConnectionHandler: socketConnectionHandler
)
let keychain = KeychainStorageMock()
let relayClient = RelayClientFactory.create(
Expand Down
59 changes: 20 additions & 39 deletions Sources/WalletConnectRelay/Dispatching.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@ final class Dispatcher: NSObject, Dispatching {
var onMessage: ((String) -> Void)?
var socket: WebSocketConnecting
var socketConnectionHandler: SocketConnectionHandler


private let defaultTimeout: Int = 5
private let relayUrlFactory: RelayUrlFactory
private let networkMonitor: NetworkMonitoring
private let logger: ConsoleLogging

private let defaultTimeout: Int = 5
/// The property is used to determine whether relay.walletconnect.org will be used
/// in case relay.walletconnect.com doesn't respond for some reason (most likely due to being blocked in the user's location).
private var fallback = false

private let socketConnectionStatusPublisherSubject = CurrentValueSubject<SocketConnectionStatus, Never>(.disconnected)

var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never> {
Expand All @@ -42,25 +38,17 @@ final class Dispatcher: NSObject, Dispatching {
socketFactory: WebSocketFactory,
relayUrlFactory: RelayUrlFactory,
networkMonitor: NetworkMonitoring,
socketConnectionType: SocketConnectionType,
logger: ConsoleLogging
socket: WebSocketConnecting,
logger: ConsoleLogging,
socketConnectionHandler: SocketConnectionHandler
) {
self.socketConnectionHandler = socketConnectionHandler
self.relayUrlFactory = relayUrlFactory
self.networkMonitor = networkMonitor
self.logger = logger

let socket = socketFactory.create(with: relayUrlFactory.create(fallback: fallback))
socket.request.addValue(EnvironmentInfo.userAgent, forHTTPHeaderField: "User-Agent")
if let bundleId = Bundle.main.bundleIdentifier {
socket.request.addValue(bundleId, forHTTPHeaderField: "Origin")
}

self.socket = socket

switch socketConnectionType {
case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket)
case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket)
}


super.init()
setUpWebSocketSession()
setUpSocketConnectionObserving()
Expand Down Expand Up @@ -90,9 +78,6 @@ final class Dispatcher: NSObject, Dispatching {
switch result {
case .failure(let error):
cancellable?.cancel()
if !socket.isConnected {
handleFallbackIfNeeded(error: error)
}
completion(error)
case .finished: break
}
Expand All @@ -101,23 +86,30 @@ final class Dispatcher: NSObject, Dispatching {
send(string, completion: completion)
})
}


func protectedSend(_ string: String) async throws {
var isResumed = false
return try await withCheckedThrowingContinuation { continuation in
protectedSend(string) { error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: ())
if !isResumed {
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: ())
}
isResumed = true
}
}
}
}

func connect() throws {
// Attempt to handle connection
try socketConnectionHandler.handleConnect()
}


func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws {
try socketConnectionHandler.handleDisconnect(closeCode: closeCode)
}
Expand All @@ -138,22 +130,11 @@ extension Dispatcher {
socket.onDisconnect = { [unowned self] error in
self.socketConnectionStatusPublisherSubject.send(.disconnected)
if error != nil {
self.socket.request.url = relayUrlFactory.create(fallback: fallback)
self.socket.request.url = relayUrlFactory.create()
}
Task(priority: .high) {
await self.socketConnectionHandler.handleDisconnection()
}
}
}

private func handleFallbackIfNeeded(error: NetworkError) {
if error == .connectionFailed && socket.request.url?.host == NetworkConstants.defaultUrl {
logger.debug("[WebSocket] - Fallback to \(NetworkConstants.fallbackUrl)")
fallback = true
socket.request.url = relayUrlFactory.create(fallback: fallback)
Task(priority: .high) {
await self.socketConnectionHandler.handleDisconnection()
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/WalletConnectRelay/PackageConfig.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version": "1.18.6"}
{"version": "1.18.7"}
2 changes: 1 addition & 1 deletion Sources/WalletConnectRelay/RelayClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public final class RelayClient {

logger.debug("[Publish] Sending payload on topic: \(topic)")

Task {try await dispatcher.protectedSend(message)}
try await dispatcher.protectedSend(message)

return try await withUnsafeThrowingContinuation { continuation in
var cancellable: AnyCancellable?
Expand Down
23 changes: 20 additions & 3 deletions Sources/WalletConnectRelay/RelayClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,30 @@ public struct RelayClientFactory {
projectId: projectId,
socketAuthenticator: socketAuthenticator
)
let socket = socketFactory.create(with: relayUrlFactory.create())
socket.request.addValue(EnvironmentInfo.userAgent, forHTTPHeaderField: "User-Agent")
if let bundleId = Bundle.main.bundleIdentifier {
socket.request.addValue(bundleId, forHTTPHeaderField: "Origin")
}
let socketFallbackHandler = SocketUrlFallbackHandler(
relayUrlFactory: relayUrlFactory,
logger: logger,
socket: socket,
networkMonitor: networkMonitor
)
var socketConnectionHandler: SocketConnectionHandler!
switch socketConnectionType {
case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketFallbackHandler)
case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketFallbackHandler)
}

let dispatcher = Dispatcher(
socketFactory: socketFactory,
relayUrlFactory: relayUrlFactory,
relayUrlFactory: relayUrlFactory,
networkMonitor: networkMonitor,
socketConnectionType: socketConnectionType,
logger: logger
socket: socket,
logger: logger,
socketConnectionHandler: socketConnectionHandler
)

let rpcHistory = RPCHistoryFactory.createForRelay(keyValueStorage: keyValueStorage)
Expand Down
13 changes: 10 additions & 3 deletions Sources/WalletConnectRelay/RelayURLFactory.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import Foundation

struct RelayUrlFactory {
class RelayUrlFactory {
private let relayHost: String
private let projectId: String
private let socketAuthenticator: ClientIdAuthenticating

/// The property is used to determine whether relay.walletconnect.org will be used
/// in case relay.walletconnect.com doesn't respond for some reason (most likely due to being blocked in the user's location).
private var fallback: Bool = false

init(
relayHost: String,
projectId: String,
Expand All @@ -15,7 +18,11 @@ struct RelayUrlFactory {
self.socketAuthenticator = socketAuthenticator
}

func create(fallback: Bool) -> URL {
func setFallback() {
self.fallback = true
}

func create() -> URL {
var components = URLComponents()
components.scheme = "wss"
components.host = fallback ? NetworkConstants.fallbackUrl : relayHost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,60 @@ class AutomaticSocketConnectionHandler {
private let appStateObserver: AppStateObserving
private let networkMonitor: NetworkMonitoring
private let backgroundTaskRegistrar: BackgroundTaskRegistering
private let defaultTimeout: Int = 5
private let logger: ConsoleLogging
private var socketUrlFallbackHandler: SocketUrlFallbackHandler

private var publishers = Set<AnyCancellable>()
private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.automatic_socket_connection", attributes: .concurrent)

init(
socket: WebSocketConnecting,
networkMonitor: NetworkMonitoring = NetworkMonitor(),
appStateObserver: AppStateObserving = AppStateObserver(),
backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar()
backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar(),
logger: ConsoleLogging,
socketUrlFallbackHandler: SocketUrlFallbackHandler
) {
self.appStateObserver = appStateObserver
self.socket = socket
self.networkMonitor = networkMonitor
self.backgroundTaskRegistrar = backgroundTaskRegistrar
self.logger = logger
self.socketUrlFallbackHandler = socketUrlFallbackHandler

setUpStateObserving()
setUpNetworkMonitoring()

socketUrlFallbackHandler.onTryReconnect = { [unowned self] in
Task(priority: .high) {
await tryReconect()
}
}

connect()

}

func connect() {
// Attempt to handle connection
socket.connect()

// Start a timer for the fallback mechanism
let timer = DispatchSource.makeTimerSource(queue: concurrentQueue)
timer.schedule(deadline: .now() + .seconds(defaultTimeout))
timer.setEventHandler { [weak self] in
guard let self = self else {
timer.cancel()
return
}
if !self.socket.isConnected {
self.logger.debug("Connection timed out, initiating fallback...")
self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed)
}
timer.cancel()
}
timer.resume()
}

private func setUpStateObserving() {
Expand Down Expand Up @@ -73,6 +109,10 @@ class AutomaticSocketConnectionHandler {
// MARK: - SocketConnectionHandler

extension AutomaticSocketConnectionHandler: SocketConnectionHandler {
func tryReconect() async {
guard await appStateObserver.currentState == .foreground else { return }
reconnectIfNeeded()
}

func handleConnect() throws {
throw Errors.manualSocketConnectionForbidden
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
import Foundation

class ManualSocketConnectionHandler: SocketConnectionHandler {
var socket: WebSocketConnecting

init(socket: WebSocketConnecting) {
self.socket = socket
}
private let socket: WebSocketConnecting
private let logger: ConsoleLogging
private let defaultTimeout: Int = 5
private var socketUrlFallbackHandler: SocketUrlFallbackHandler
private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.manual_socket_connection", attributes: .concurrent)

init(
socket: WebSocketConnecting,
logger: ConsoleLogging,
socketUrlFallbackHandler: SocketUrlFallbackHandler) {
self.socket = socket
self.logger = logger
self.socketUrlFallbackHandler = socketUrlFallbackHandler

socketUrlFallbackHandler.onTryReconnect = { [unowned self] in
Task(priority: .high) {
await tryReconect()
}
}
}

func handleConnect() throws {
socket.connect()
// Start a timer for the fallback mechanism
let timer = DispatchSource.makeTimerSource(queue: concurrentQueue)
timer.schedule(deadline: .now() + .seconds(defaultTimeout))
timer.setEventHandler { [weak self] in
guard let self = self else {
timer.cancel()
return
}
if !self.socket.isConnected {
self.logger.debug("Connection timed out, initiating fallback...")
self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed)
}
timer.cancel()
}
timer.resume()
}

func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws {
Expand All @@ -19,4 +50,10 @@ class ManualSocketConnectionHandler: SocketConnectionHandler {
// No operation
// ManualSocketConnectionHandler does not support reconnection logic
}

func tryReconect() async {
if !socket.isConnected {
socket.connect()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ protocol SocketConnectionHandler {
func handleConnect() throws
func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws
func handleDisconnection() async
func tryReconect() async
}
Loading

0 comments on commit 1fcdbdc

Please sign in to comment.