Singleton-like behavior in Dependency

I'm making a simple chat app to explore WebSockets and to practice applying TCA. It compiles and runs as intended, however, I'm not confident whether my code uses best practices regarding my custom WebSockets dependency.

In my ChatFeature, I declared the following actions (among others):

  • receiveDelta(String): appends the String to a list in the feature's state
  • sendButtonTapped: appends the contents of a TextField binding to the same list, empty-strings the binding, and most importantly, has a run effect:
return .run { send in
  for await event in await self.chat.send(message) {
    await send(.receiveDelta(event))
  }
}
  • viewAboutToAppear: calls connect of the WebSockets dependency to connect to wss://ws.postman-echo.com/raw/ which is a simple WebSockets server that just echoes whatever you send it.
  • viewAboutToDisappear: calls disconnect of the WebSockets dependency

Now, in my ChatClient, which implements the WebSockets dependency, I have:

struct ChatClient {
    private static var task: URLSessionWebSocketTask? = nil
    var connect: (_ url: URL) -> Void
    var disconnect: () -> Void
    var send: (_ message: Message) async -> AsyncStream<String>
}

The live implementation of ChatClient.connect assigns a new .webSocketTask to ChatClient.task and calls task!.resume() to start the connection.

The live implementation of ChatClient.disconnect calls task?.cancel(...) and nils out task.

Now here are my questions:

  1. Is it appropriate to persist the connection in private static var task of ChatClient?
  2. Is it appropriate to use .onAppear and .onDisappear to send the .viewAboutToAppear and .viewAboutToDisappear actions to the viewStore? These actions connect/disconnect by working with ChatClient.task.

Here's the full code. I realize this is more of a code review question but I want to be sure I am designing the dependency correctly before proceeding.