Search code examples
swiftwebsocketasync-awaitcombine

Receive real-time updates like WebSocket but via Async/Await or Combine


I am trying to achieve an update for a simple counter using a network request, but want to get real time updates. I can only think of using WebSocket.

Can I get a similar interaction using Structured Concurrency or Combine ? Maybe I should just use TimelineView and do a request every second ?


Solution

  • The question of Swift concurrency vs legacy patterns has nothing to do with whether you use WebSocket or HTTPS. You can use Swift concurrency with either approach.

    1. If your backend is using web sockets, the client side can easily use Swift concurrency. So, if your backend has implemented real-time communications, you can use URLSession WebSocket (discussed in WWDC 2019 video Advances in Networking, Part 1). E.g., you can create the socket:

      let socket = URLSession.shared.webSocketTask(with: url)
      socket.resume()
      

      Then you can use Swift concurrency to send:

      try await socket.send(…)
      

      And to receive:

      let message = try await socket.receive()
      
    2. FWIW, polling with HTTPS GET every second (or whatever) would best be considered an antipattern.

    3. If you want to implement (near) real-time communications without diving into sockets, you can have your server post an APNs push notification when there is a message ready for the client. Regarding the nature of this APNs payload, it varies depending upon the nature of the message:

      • If the payload is a short text message, you might include the content of the message to be sent to the client in the payload of the push notification. This is pattern has the advantage that the client app need not need be running in order for the user to see the message alert on their device. So the push payload consists of both the alert message (in case the user is not currently using your app), and a data payload (which your app can parse and process if you do not want it to present a system alert). Or,

      • If the payload being sent beyond the scope of what can be included in a UserNotifications alert (e.g., a large asset or a streaming URL), then the push payload with an alert saying “You have a message”, but also include some identifier and have the client then do a HTTPS GET request for the content upon receipt of the push notification (which is what I do for larger payloads such as an asset like an image).

      Either way, your app can intercept these push notifications and it is up to you whether you show a system alert or whether your app will just seamlessly handle it.

    In my real-time messaging solutions, I have used a hybrid approach, employing both sockets (for real-time interaction within the app) and push notifications (to alert user of new content when the app is not running). But if you do not want to get caught up in the backend infrastructure for scalable web sockets, the push paradigm can still achieve (near) real-time messaging, without engaging in the horribly inefficient polling pattern.

    But again, the backend architecture you choose for the real-time messaging is largely independent of whether you use Swift concurrency (or Combine) or not. You can use Swift concurrency or Combine with pretty much any broader messaging paradigm.