-
Notifications
You must be signed in to change notification settings - Fork 191
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
base: develop
Are you sure you want to change the base?
Changes from 5 commits
afce442
e49086b
e6c609b
274438e
7f57858
4361cea
70f2dd3
f9b74c8
10d8592
78d0544
a457c5f
90251da
9af6061
219a24c
ac6f2e2
ab58b21
2f5b54c
95176e4
9ace2d0
5913314
7c8ca64
6203975
2d78626
1bc4842
2340bb3
5642411
4bd0227
748dc66
366a891
cf8168e
1ab4dbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
This file was deleted.
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 { | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we're you're setting isConnected = false on reconnect? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, strange There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the goal of this didSet closure? I can see There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's used for the updating socket url (new |
||
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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Radek is correct 😅 Agree this looks strange |
||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 onURLSessionWebSocketTask
internal types likeMessage
andCloseCode
. But your suggestion is also valid, let's discuss on sync.