joshuaauerbachwatson / unigame

The core client layer for apps that use the unigame server, covering the game-agnostic aspects
0 stars 0 forks source link

Ensure MainActor is used for all UI operations given Swift Concurrency #19

Closed joshuaauerbachwatson closed 1 week ago

joshuaauerbachwatson commented 2 weeks ago

There is significant asynchronous behavior in unigame due to the Communicator and CommunicatorDelegate pattern. Incoming events from the communicators cause UI updates. Traditionally, this was made to work by using DispatchQueue.main.async to switch from a possible background task context to the correct main dispatch queue.

It appears that more and more of SwiftUI and other Swift frameworks are using the new Swift Concurrency (async, await, etc) rather than dispatch queues. It seems that the two models don't mix well and online doc seems to discourage the use of the queue switching as a solution to this problem. Rather, they want you to major in Swift Concurrency across the board.

But, the CommunicatorDelegate methods seem hard to adapt to that model. There are several different methods representing incoming events. We don't know what order these will arrive in or how many times they will be repeated. So, it is not like turning a single callback into async/await by using a continuation object. We probably need to

  1. Wrap all incoming events in an enum (variant) so that they can be awaited at a single dispatch point.
  2. Use some kind of asynchronous iteration to wait at that dispatch point.

The AsyncStream seems to fit the case pretty well.

Separately we can consider to what extent the underlying frameworks used by the two communicators already do some form of Swift Concurrency. That is, is there a version of Multi-Peer Connectivity that uses async/await and or a version of URLWebSocketTask that does so?

In the latter case, yes. There is an async version of URLWebSocketTask.receive. I don't see any evidence of that with multi-peer.

Doing all of this as outlined could be a pretty big change so I think I should first proceed with testing and trying to get things to work without doing too much violence to the code (including experimenting with DispatchQueue.main.async).

joshuaauerbachwatson commented 1 week ago

I did change over to the logic described here. It works. It is no longer clear to me that it wasn't actually ok before (I have meanwhile fixed several out-and-out bugs which were keeping things from working before). I am keeping the change, though, since it seems more in keeping with how you are supposed to do things in the SwiftUI world.

joshuaauerbachwatson commented 1 week ago

I seem to have gotten this aspect to an acceptable point. Closing, at least for now.