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

[Dispatching] Native socket support #911

Open
wants to merge 31 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
afce442
Add native WebSocket support
alexander-lsvk Jun 15, 2023
e49086b
Remove test data
alexander-lsvk Jun 15, 2023
e6c609b
Merge remote-tracking branch 'origin/develop' into native-socket
alexander-lsvk Jun 15, 2023
274438e
Fix merge issues
alexander-lsvk Jun 15, 2023
7f57858
Merge branch 'develop' into native-socket
alexander-lsvk Jun 16, 2023
4361cea
Merge branch 'develop' into native-socket
alexander-lsvk Jul 2, 2023
70f2dd3
Fix merge errors
alexander-lsvk Jul 2, 2023
f9b74c8
Fix merge errors
alexander-lsvk Jul 2, 2023
10d8592
Update WebSocketConnecting protocol
alexander-lsvk Jul 5, 2023
78d0544
Merge branch 'develop' into native-socket
alexander-lsvk Jul 5, 2023
a457c5f
Merge branch 'develop' into native-socket
alexander-lsvk Jul 12, 2023
90251da
Fix merge conflicts
alexander-lsvk Jul 12, 2023
9af6061
Update mocks visibility
alexander-lsvk Jul 12, 2023
219a24c
Merge branch 'develop' into native-socket
alexander-lsvk Jul 13, 2023
ac6f2e2
Fix native socket init issue
alexander-lsvk Jul 13, 2023
ab58b21
Fix socket disconnection
alexander-lsvk Jul 14, 2023
2f5b54c
Merge branch 'develop' into native-socket
alexander-lsvk Jul 14, 2023
95176e4
Fix native socket issue
alexander-lsvk Jul 16, 2023
9ace2d0
Add fallback to walletconnect.org
alexander-lsvk Jul 18, 2023
5913314
Fix tests
alexander-lsvk Jul 18, 2023
7c8ca64
Merge branch 'develop' into native-socket
alexander-lsvk Jul 31, 2023
6203975
Support Starscream
alexander-lsvk Sep 7, 2023
2d78626
Merge branch 'develop' into native-socket
alexander-lsvk Sep 7, 2023
1bc4842
Update socket client
alexander-lsvk Sep 12, 2023
2340bb3
Merge branch 'develop' into native-socket
alexander-lsvk Sep 12, 2023
5642411
Update tests
alexander-lsvk Sep 12, 2023
4bd0227
Fix tests
alexander-lsvk Sep 12, 2023
748dc66
Update tests
alexander-lsvk Sep 12, 2023
366a891
Update tests
alexander-lsvk Sep 14, 2023
cf8168e
Update
alexander-lsvk Oct 23, 2023
1ab4dbe
Merge branch 'develop' into native-socket
alexander-lsvk Oct 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Example/DApp/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
private let authCoordinator = AuthCoordinator()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory())
Networking.configure(projectId: InputConfig.projectId)
Auth.configure(crypto: DefaultCryptoProvider())

let metadata = AppMetadata(
Expand Down
61 changes: 0 additions & 61 deletions Example/ExampleApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,6 @@
"version": null
}
},
{
"package": "Starscream",
"repositoryURL": "https://github.com/daltoniam/Starscream",
"state": {
"branch": null,
"revision": "a063fda2b8145a231953c20e7a646be254365396",
"version": "3.1.2"
}
},
{
"package": "swift-qrcode-generator",
"repositoryURL": "https://github.com/dagronf/swift-qrcode-generator",
Expand Down
2 changes: 1 addition & 1 deletion Example/IntegrationTests/Auth/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class AuthTests: XCTestCase {
func makeClients(prefix: String, iatProvider: IATProvider) -> (PairingClient, AuthClient) {
let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug)
let keychain = KeychainStorageMock()
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger)
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, logger: logger)
let keyValueStorage = RuntimeKeyValueStorage()

let networkingClient = NetworkingClientFactory.create(
Expand Down
2 changes: 1 addition & 1 deletion Example/IntegrationTests/Chat/ChatTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ final class ChatTests: XCTestCase {
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug)
let keychain = KeychainStorageMock()
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger)
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, logger: logger)
let keyValueStorage = RuntimeKeyValueStorage()
let networkingInteractor = NetworkingClientFactory.create(
relayClient: relayClient,
Expand Down
1 change: 0 additions & 1 deletion Example/IntegrationTests/History/HistoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ final class HistoryTests: XCTestCase {
projectId: InputConfig.projectId,
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
logger: ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug))
}

Expand Down
1 change: 0 additions & 1 deletion Example/IntegrationTests/Pairing/PairingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ final class PairingTests: XCTestCase {
projectId: InputConfig.projectId,
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
logger: relayLogger)

let networkingClient = NetworkingClientFactory.create(
Expand Down
1 change: 0 additions & 1 deletion Example/IntegrationTests/Push/PushTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ final class PushTests: XCTestCase {
projectId: InputConfig.projectId,
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
logger: relayLogger)

let networkingClient = NetworkingClientFactory.create(
Expand Down
1 change: 0 additions & 1 deletion Example/IntegrationTests/Sign/SignClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ final class SignClientTests: XCTestCase {
projectId: InputConfig.projectId,
keyValueStorage: RuntimeKeyValueStorage(),
keychainStorage: keychain,
socketFactory: DefaultSocketFactory(),
socketConnectionType: .automatic,
logger: logger
)
Expand Down
2 changes: 1 addition & 1 deletion Example/IntegrationTests/Sync/SyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ final class SyncTests: XCTestCase {
let kms = KeyManagementService(keychain: keychain)
let derivationService = SyncDerivationService(syncStorage: syncSignatureStore, bip44: DefaultBIP44Provider(), kms: kms)
let logger = ConsoleLogger(suffix: suffix, loggingLevel: .debug)
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger)
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, logger: logger)
let networkingInteractor = NetworkingClientFactory.create(
relayClient: relayClient,
logger: logger,
Expand Down
10 changes: 5 additions & 5 deletions Example/RelayIntegrationTests/RelayClientEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation
import Combine
import XCTest
import WalletConnectUtils
import Starscream
@testable import WalletConnectRelay

private class RelayKeychainStorageMock: KeychainStorageProtocol {
Expand All @@ -15,9 +14,9 @@ private class RelayKeychainStorageMock: KeychainStorageProtocol {
}

class WebSocketFactoryMock: WebSocketFactory {
private let webSocket: WebSocket
private let webSocket: WebSocketClient

init(webSocket: WebSocket) {
init(webSocket: WebSocketClient) {
self.webSocket = webSocket
}

Expand All @@ -41,9 +40,10 @@ final class RelayClientEndToEndTests: XCTestCase {
projectId: InputConfig.projectId,
socketAuthenticator: socketAuthenticator
)
let socket = WebSocket(url: urlFactory.create())
let webSocketFactory = WebSocketFactoryMock(webSocket: socket)
let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug)
let socket = WebSocketClient(url: urlFactory.create(), logger: ConsoleLogger(suffix: prefix, loggingLevel: .debug))
let webSocketFactory = WebSocketFactoryMock(webSocket: socket)

let dispatcher = Dispatcher(
socketFactory: webSocketFactory,
relayUrlFactory: urlFactory,
Expand Down
11 changes: 0 additions & 11 deletions Example/Shared/DefaultSocketFactory.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@ import Auth
import Web3Modal

struct ThirdPartyConfigurator: Configurator {

func configure() {

func configure() {
let metadata = AppMetadata(
name: "Showcase App",
description: "Showcase description",
url: "example.wallet",
icons: ["https://avatars.githubusercontent.com/u/37784886"]
)

Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory())
Networking.configure(projectId: InputConfig.projectId)
Auth.configure(crypto: DefaultCryptoProvider())
Web3Modal.configure(projectId: InputConfig.projectId, metadata: metadata)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Web3Inbox
struct ThirdPartyConfigurator: Configurator {

func configure() {
Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory())
Networking.configure(projectId: InputConfig.projectId)

let metadata = AppMetadata(
name: "Example Wallet",
Expand Down
4 changes: 0 additions & 4 deletions Sources/WalletConnectNetworking/Networking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,20 @@ public class Networking {
/// - Parameters:
/// - relayHost: relay host
/// - projectId: project id
/// - socketFactory: web socket factory
/// - socketConnectionType: socket connection type
static public func configure(
relayHost: String = "relay.walletconnect.com",
projectId: String,
socketFactory: WebSocketFactory,
socketConnectionType: SocketConnectionType = .automatic
) {
Networking.config = Networking.Config(
relayHost: relayHost,
projectId: projectId,
socketFactory: socketFactory,
socketConnectionType: socketConnectionType
)
Relay.configure(
relayHost: relayHost,
projectId: projectId,
socketFactory: socketFactory,
socketConnectionType: socketConnectionType)
}
}
1 change: 0 additions & 1 deletion Sources/WalletConnectNetworking/NetworkingConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extension Networking {
struct Config {
let relayHost: String
let projectId: String
let socketFactory: WebSocketFactory
let socketConnectionType: SocketConnectionType
}
}
3 changes: 0 additions & 3 deletions Sources/WalletConnectRelay/Relay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public class Relay {
return RelayClient(
relayHost: config.relayHost,
projectId: config.projectId,
socketFactory: config.socketFactory,
socketConnectionType: config.socketConnectionType
)
}()
Expand All @@ -33,13 +32,11 @@ public class Relay {
static public func configure(
relayHost: String = "relay.walletconnect.com",
projectId: String,
socketFactory: WebSocketFactory,
socketConnectionType: SocketConnectionType = .automatic
) {
Relay.config = Relay.Config(
relayHost: relayHost,
projectId: projectId,
socketFactory: socketFactory,
socketConnectionType: socketConnectionType
)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnectRelay/RelayClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public final class RelayClient {
projectId: String,
keyValueStorage: KeyValueStorage = UserDefaults.standard,
keychainStorage: KeychainStorageProtocol = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk"),
socketFactory: WebSocketFactory,
socketConnectionType: SocketConnectionType = .automatic,
logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug)
) {
Expand All @@ -89,8 +88,9 @@ public final class RelayClient {
projectId: projectId,
socketAuthenticator: socketAuthenticator
)
let webSocketClientFactory = WebSocketClientFactory()
let dispatcher = Dispatcher(
socketFactory: socketFactory,
socketFactory: webSocketClientFactory,
relayUrlFactory: relayUrlFactory,
socketConnectionType: socketConnectionType,
logger: logger
Expand Down
1 change: 0 additions & 1 deletion Sources/WalletConnectRelay/RelayConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extension Relay {
struct Config {
let relayHost: String
let projectId: String
let socketFactory: WebSocketFactory
let socketConnectionType: SocketConnectionType
}
}
118 changes: 118 additions & 0 deletions Sources/WalletConnectRelay/WebSocketClient/WebSocketClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import Foundation

enum WebSocketClientError: Error {
case errorWithCode(URLSessionWebSocketTask.CloseCode)
}

struct WebSocketClientFactory: WebSocketFactory {
func create(with url: URL) -> WebSocketConnecting {
return WebSocketClient(url: url, logger: ConsoleLogger(loggingLevel: .debug))
}
}

final class WebSocketClient: NSObject, WebSocketConnecting {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need to conform to WebSocketConnecting protocol?
I was a starscream requirement
wouldn't it be better to refactor Dispatcher and socket would conform to this protocol?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this protocol is more abstract in terms of testing. In case of conforming to URLSessionWebSocketTask we will need to depend on URLSessionWebSocketTask internal types like Message and CloseCode. But your suggestion is also valid, let's discuss on sync.

private var socket: URLSessionWebSocketTask? = nil
private var url: URL
private let logger: ConsoleLogging

init(url url: URL, logger: ConsoleLogging) {
self.url = url
self.logger = logger
self.isConnected = false
self.request = URLRequest(url: url)
super.init()
}

func reconnect() {
let configuration = URLSessionConfiguration.default
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue())
let urlRequest = URLRequest(url: url)
socket = urlSession.webSocketTask(with: urlRequest)

isConnected = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we're you're setting isConnected = false on reconnect?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, strange

Copy link
Contributor Author

@alexander-lsvk alexander-lsvk Jul 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we are not actually connected yet but disconnected already. It will happen here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit concerned about handling the state of this flag properly, doesn't websocketTask or anything expose the state from urlsession itself?

connect()
}

// MARK: - WebSocketConnecting
var isConnected: Bool
var onConnect: (() -> Void)?
var onDisconnect: ((Error?) -> Void)?
var onText: ((String) -> Void)?
var request: URLRequest {
didSet {
if let url = request.url {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the goal of this didSet closure? I can see request is assigned only once in the init.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used for the updating socket url (new auth parameter) when automatic reconnect happens. It will be set here.

let configuration = URLSessionConfiguration.default
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue())
let urlRequest = URLRequest(url: url)
socket = urlSession.webSocketTask(with: urlRequest)

isConnected = false
self.url = url
}
}
}

func connect() {
logger.debug("[WebSocketClient]: Connect called")
socket?.resume()
}

func disconnect() {
logger.debug("[WebSocketClient]: Disconnect called")
socket?.cancel()
}

func write(string: String, completion: (() -> Void)?) {
let message = URLSessionWebSocketTask.Message.string(string)
socket?.send(message) { _ in
completion?()
}
}
}

// MARK: - URLSessionWebSocketDelegate
extension WebSocketClient: URLSessionWebSocketDelegate {
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {
isConnected = true
logger.debug("[WebSocketClient]: Connected")
onConnect?()
receiveMessage()
}

func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
isConnected = false
logger.debug("[WebSocketClient]: Did close with code: \(closeCode)")
onDisconnect?(WebSocketClientError.errorWithCode(closeCode))
}

func receiveMessage() {
socket?.receive { [weak self] result in
guard let self else {
return
}
switch result {
case .failure(let error):
self.logger.debug("[WebSocketClient]: Error receiving: \(error)")
self.isConnected = false

case .success(let message):
switch message {
case .string(let messageString):
self.logger.debug("[WebSocketClient]: Received message: \(messageString)")
self.onText?(messageString)

case .data(let data):
self.logger.debug("[WebSocketClient]: Received data: \(data.description)")
self.onText?(data.description)

default:
self.logger.debug("[WebSocketClient]: Received unknown data")
}
}

if self.isConnected == true {
self.receiveMessage()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it safe to call method from it self here? How it's working?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also got confused, but apparently, this is by design from Apple. It needs to be called to receive further messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Radek is correct 😅 Agree this looks strange

}
}
}
}
2 changes: 1 addition & 1 deletion Sources/Web3Modal/Modal/Modal+Previews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct ModalContainerView_Previews: PreviewProvider {
icons: ["https://avatars.githubusercontent.com/u/37784886"]
)

Networking.configure(projectId: projectId, socketFactory: WebSocketFactoryMock())
Networking.configure(projectId: projectId)
Web3Modal.configure(projectId: projectId, metadata: metadata)
}

Expand Down
7 changes: 2 additions & 5 deletions Sources/Web3Modal/Modal/ModalContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import SwiftUI

@available(iOS 14.0, *)
public struct ModalContainerView: View {

@Environment(\.presentationMode) var presentationMode

@State var showModal: Bool = false

public var body: some View {


public var body: some View {
VStack(spacing: -10) {

Color.thickOverlay
.colorScheme(.light)
.onTapGesture {
Expand Down
1 change: 0 additions & 1 deletion Sources/Web3Modal/Modal/ModalInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ protocol ModalSheetInteractor {
}

final class DefaultModalSheetInteractor: ModalSheetInteractor {

lazy var sessionSettlePublisher: AnyPublisher<Session, Never> = Web3Modal.instance.sessionSettlePublisher
lazy var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never> = Web3Modal.instance.sessionRejectionPublisher

Expand Down