2

So I'm using iOS 13's URLSessionWebSocket functionality.

I have a screen that should send a ping to another socket every X seconds. Everything works fine until the user moves the app to the background.

Since I don't need to perform any requests in the background, I'm trying to suspend all of the URLSessionWebSocketTasks until the app is back in the foreground.

In my code, I have several tasks stored in socketConnections dictionary. I also have a BehaviorSubject named "inForeground" that indicates the state. I'm listening to UIScene's background/foreground notifications and doing this:

    @objc func appDidMoveToBackground(_ notification: Notification) {
        inForeground.onNext(false)
        for (taskId, task) in socketConnections {
            task.suspend()
        }
    }
    
    @objc func appDidBecomeActive(_ notification: Notification) {
        inForeground.onNext(true)
        for (id, task) in socketConnections {
            task.resume()
            setReceiveHandler(id)
        }
    }
    
    private func setReceiveHandler(_ taskId: String) {
        guard let foregroundState = try? self.inForeground.value(), foregroundState else {
            print("not in foreground")
            return
        }
        socketConnections[taskId]?.receive { [unowned self] result in
            do {
                let message = try result.get()
                print("\(message)")
                self.setReceiveHandler(taskId)
            } catch let error {
                print(error.localizedDescription)
            }
        }
    }
    

When I move to background and then foreground, I still get errors:

nw_read_request_report [C16] Receive failed with error "Software caused connection abort"

and

[websocket] Read completed with an error Software caused connection abort

and then:

Connection 16: received failure notification

Any ideas what could go wrong?

rot_sh
  • 41
  • 4

1 Answers1

3

The short answer is that you can not suspend and resume a connection if the application goes to background. You can either keep the connection alive using backgroundTasks, or you can re-structure the logic of your app so it restart your connection from scratch(or both).

The explanation: When you open a socket connection the kernel/OS is allocating and managing resources(memory, ports) on behalf of your application. This is freed* when the application enters background so that it can be used by other apps. So the open connection you had, after the application enters background is not long valid and can not be re-used, with some very specific exceptions (VOIP apps).

If you choose to ask for more time the OS will allow the connection to stay alive you can find details in proper-use-of-beginbackgroundtaskwithexpirationhandler. But because the background execution time is limited you should build your app is a way that the connections are restarted when needed.

Christos Koninis
  • 1,499
  • 1
  • 15
  • 28