I'm having trouble freeing my URLSessionWebSocketTask when I am done using it. This only happens if I try to use the async/await version of the interface, the normal closure-based version works.
My application does to much to share the full source but this class should show the error:
class ExoWebSocket {
var webSocket: URLSessionWebSocketTask?
var session: URLSession?
init(ip: String) {
var request = URLRequest(url: URL(string: "ws://\(ip)/_EXOSocket/")!)
request.addValue("EXOsocket", forHTTPHeaderField: "Sec-WebSocket-Protocol")
request.timeoutInterval = 1.0
session = URLSession(configuration: .default, delegate: nil, delegateQueue: nil)
webSocket = session?.webSocketTask(with: request)
webSocket?.resume()
taskReceive() // leaks one __NSURLSessionWebSocketTask object
//closureReceive() // works as expected
}
deinit {
webSocket?.cancel(with: .normalClosure, reason: nil)
}
private func closureReceive() {
webSocket?.receive { [weak self] result in
switch result {
case .success(let message):
switch message {
case .string(let text): print("text: \(text)")
case .data(let data): print("data count: \(data.count)")
default: print("Unknown type received from WebSocket")
}
self?.closureReceive()
case .failure(let error):
print("Error when receiving \(error)")
self?.webSocket = nil
}
}
}
private func taskReceive() {
Task { [weak self] in
while self?.webSocket != nil {
let message = try? await self?.webSocket?.receive()
switch message {
case .string(let text): print("text: \(text)")
case .data(let data): print("data count: \(data.count)")
default: print("Unknown type received from WebSocket")
}
}
}
}
}
I create an ExoWebSocket object in a ViewController and then exit the ViewController. I see that the deinit
function is called, but the webSocket?.cancel(...)
does not cause the try? await receive
to throw or exit.
Is there anything obvious that I should do differently?
Tested with Xcode 13.4 / iPad OS 15.3 and Xcode 14 beta 6 / iPad OS 16.1 beta.