Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Network monitor in dispatcher #1284

Merged
merged 10 commits into from
Jan 24, 2024
Merged
1 change: 1 addition & 0 deletions Example/IntegrationTests/Auth/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ final class AuthTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: logger)

let networkingClient = NetworkingClientFactory.create(
Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Chat/ChatTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ final class ChatTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: logger)

let networkingInteractor = NetworkingClientFactory.create(
Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Pairing/PairingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ final class PairingTests: XCTestCase {
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: logger)

let networkingClient = NetworkingClientFactory.create(
Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Push/NotifyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ final class NotifyTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: relayLogger)

let networkingClient = NetworkingClientFactory.create(
Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Sign/SignClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ final class SignClientTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: logger
)

Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Sync/SyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ final class SyncTests: XCTestCase {
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: logger)
let networkingInteractor = NetworkingClientFactory.create(
relayClient: relayClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class XPlatformW3WTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
networkMonitor: NetworkMonitor(),
logger: relayLogger
)

Expand Down
5 changes: 4 additions & 1 deletion Example/RelayIntegrationTests/RelayClientEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ final class RelayClientEndToEndTests: XCTestCase {
)
let socket = WebSocket(url: urlFactory.create(fallback: false))
let webSocketFactory = WebSocketFactoryMock(webSocket: socket)
let networkMonitor = NetworkMonitor()
let dispatcher = Dispatcher(
socketFactory: webSocketFactory,
relayUrlFactory: urlFactory,
networkMonitor: networkMonitor,
socketConnectionType: .manual,
logger: logger
)
Expand All @@ -57,7 +59,8 @@ final class RelayClientEndToEndTests: XCTestCase {
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
socketConnectionType: .manual,
socketConnectionType: .manual,
networkMonitor: networkMonitor,
logger: logger
)
let clientId = try! relayClient.getClientId()
Expand Down
22 changes: 16 additions & 6 deletions Sources/WalletConnectNetworking/NetworkingInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ public class NetworkingInteractor: NetworkInteracting {
public var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never>
public var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>

private let networkMonitor: NetworkMonitoring

public init(
relayClient: RelayClient,
serializer: Serializing,
Expand All @@ -43,8 +41,7 @@ public class NetworkingInteractor: NetworkInteracting {
self.rpcHistory = rpcHistory
self.logger = logger
self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher
self.networkMonitor = NetworkMonitor()
self.networkConnectionStatusPublisher = networkMonitor.networkConnectionStatusPublisher
self.networkConnectionStatusPublisher = relayClient.networkConnectionStatusPublisher
setupRelaySubscribtion()
}

Expand Down Expand Up @@ -205,8 +202,21 @@ public class NetworkingInteractor: NetworkInteracting {

public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
try rpcHistory.set(request, forTopic: topic, emmitedBy: .local)
let message = try serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType)
try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl)

do {
let message = try serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType)

try await relayClient.publish(topic: topic,
payload: message,
tag: protocolMethod.requestConfig.tag,
prompt: protocolMethod.requestConfig.prompt,
ttl: protocolMethod.requestConfig.ttl)
} catch {
if let id = request.id {
rpcHistory.delete(id: id)
flypaper0 marked this conversation as resolved.
Show resolved Hide resolved
}
throw error
}
}

public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
Expand Down
16 changes: 12 additions & 4 deletions Sources/WalletConnectRelay/Dispatching.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Combine

protocol Dispatching {
var onMessage: ((String) -> Void)? { get set }
var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> { get }
var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never> { get }
func send(_ string: String, completion: @escaping (Error?) -> Void)
func protectedSend(_ string: String, completion: @escaping (Error?) -> Void)
Expand All @@ -17,8 +18,9 @@ final class Dispatcher: NSObject, Dispatching {
var socketConnectionHandler: SocketConnectionHandler

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).
Expand All @@ -30,15 +32,21 @@ final class Dispatcher: NSObject, Dispatching {
socketConnectionStatusPublisherSubject.eraseToAnyPublisher()
}

var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> {
networkMonitor.networkConnectionStatusPublisher
}

private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.dispatcher", attributes: .concurrent)

init(
socketFactory: WebSocketFactory,
relayUrlFactory: RelayUrlFactory,
networkMonitor: NetworkMonitoring,
socketConnectionType: SocketConnectionType,
logger: ConsoleLogging
) {
self.relayUrlFactory = relayUrlFactory
self.networkMonitor = networkMonitor
self.logger = logger

let socket = socketFactory.create(with: relayUrlFactory.create(fallback: fallback))
Expand Down Expand Up @@ -69,13 +77,13 @@ final class Dispatcher: NSObject, Dispatching {
}

func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) {
guard !socket.isConnected else {
guard !socket.isConnected || !networkMonitor.isConnected else {
return send(string, completion: completion)
}

var cancellable: AnyCancellable?
cancellable = socketConnectionStatusPublisher
.filter { $0 == .connected }
cancellable = Publishers.CombineLatest(socketConnectionStatusPublisher, networkConnectionStatusPublisher)
.filter { $0.0 == .connected && $0.1 == .connected }
.setFailureType(to: NetworkError.self)
.timeout(.seconds(defaultTimeout), scheduler: concurrentQueue, customError: { .webSocketNotConnected })
flypaper0 marked this conversation as resolved.
Show resolved Hide resolved
.sink(receiveCompletion: { [unowned self] result in
Expand Down
7 changes: 6 additions & 1 deletion Sources/WalletConnectRelay/NetworkMonitoring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum NetworkConnectionStatus {
}

public protocol NetworkMonitoring: AnyObject {
var isConnected: Bool { get }
var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> { get }
}

Expand All @@ -16,7 +17,11 @@ public final class NetworkMonitor: NetworkMonitoring {
private let workerQueue = DispatchQueue(label: "com.walletconnect.sdk.network.monitor")

private let networkConnectionStatusPublisherSubject = CurrentValueSubject<NetworkConnectionStatus, Never>(.connected)


public var isConnected: Bool {
return networkConnectionStatusPublisherSubject.value == .connected
}

public var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> {
networkConnectionStatusPublisherSubject
.share()
Expand Down
12 changes: 8 additions & 4 deletions Sources/WalletConnectRelay/RelayClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public final class RelayClient {
dispatcher.socketConnectionStatusPublisher
}

public var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> {
dispatcher.networkConnectionStatusPublisher
}

private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String, publishedAt: Date), Never>()

private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, [String]), Never>()
Expand Down Expand Up @@ -165,9 +169,9 @@ public final class RelayClient {
}
}

public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) {
public func unsubscribe(topic: String, completion: ((Error?) -> Void)?) {
guard let subscriptionId = subscriptions[topic] else {
completion(Errors.subscriptionIdNotFound)
completion?(Errors.subscriptionIdNotFound)
return
}
logger.debug("Unsubscribing from topic: \(topic)")
Expand All @@ -179,12 +183,12 @@ public final class RelayClient {
dispatcher.protectedSend(message) { [weak self] error in
if let error = error {
self?.logger.debug("Failed to unsubscribe from topic")
completion(error)
completion?(error)
} else {
self?.concurrentQueue.async(flags: .barrier) {
self?.subscriptions[topic] = nil
}
completion(nil)
completion?(nil)
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion Sources/WalletConnectRelay/RelayClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ public struct RelayClientFactory {

let logger = ConsoleLogger(prefix: "🚄" ,loggingLevel: .off)

let networkMonitor = NetworkMonitor()

return RelayClientFactory.create(
relayHost: relayHost,
projectId: projectId,
keyValueStorage: keyValueStorage,
keychainStorage: keychainStorage,
socketFactory: socketFactory,
socketConnectionType: socketConnectionType,
networkMonitor: networkMonitor,
logger: logger
)
}
Expand All @@ -39,6 +42,7 @@ public struct RelayClientFactory {
keychainStorage: KeychainStorageProtocol,
socketFactory: WebSocketFactory,
socketConnectionType: SocketConnectionType = .automatic,
networkMonitor: NetworkMonitoring,
logger: ConsoleLogging
) -> RelayClient {

Expand All @@ -52,9 +56,11 @@ public struct RelayClientFactory {
projectId: projectId,
socketAuthenticator: socketAuthenticator
)

let dispatcher = Dispatcher(
socketFactory: socketFactory,
relayUrlFactory: relayUrlFactory,
relayUrlFactory: relayUrlFactory,
networkMonitor: networkMonitor,
socketConnectionType: socketConnectionType,
logger: logger
)
Expand Down
4 changes: 3 additions & 1 deletion Tests/RelayerTests/DispatcherTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ final class DispatcherTests: XCTestCase {
networkMonitor = NetworkMonitoringMock()
let defaults = RuntimeKeyValueStorage()
let logger = ConsoleLoggerMock()
let networkMonitor = NetworkMonitoringMock()
let keychainStorageMock = DispatcherKeychainStorageMock()
let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychainStorageMock, logger: logger)
let socketAuthenticator = ClientIdAuthenticator(clientIdStorage: clientIdStorage)
Expand All @@ -72,7 +73,8 @@ final class DispatcherTests: XCTestCase {
)
sut = Dispatcher(
socketFactory: webSocketFactory,
relayUrlFactory: relayUrlFactory,
relayUrlFactory: relayUrlFactory,
networkMonitor: networkMonitor,
socketConnectionType: .manual,
logger: ConsoleLoggerMock()
)
Expand Down
4 changes: 4 additions & 0 deletions Tests/RelayerTests/Mocks/DispatcherMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import Combine
@testable import WalletConnectRelay

class DispatcherMock: Dispatching {

private var publishers = Set<AnyCancellable>()
private let socketConnectionStatusPublisherSubject = CurrentValueSubject<SocketConnectionStatus, Never>(.disconnected)
var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never> {
return socketConnectionStatusPublisherSubject.eraseToAnyPublisher()
}
var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> {
return Just(.connected).eraseToAnyPublisher()
}

var sent = false
var lastMessage: String = ""
Expand Down
4 changes: 4 additions & 0 deletions Tests/RelayerTests/Mocks/NetworkMonitoringMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import Combine
@testable import WalletConnectRelay

class NetworkMonitoringMock: NetworkMonitoring {
var isConnected: Bool {
return true
}

var networkConnectionStatusPublisher: AnyPublisher<WalletConnectRelay.NetworkConnectionStatus, Never> {
networkConnectionStatusPublisherSubject.eraseToAnyPublisher()
}
Expand Down