Async await for closure -> spinlock?

I am maintaining a legacy code base which will use SwiftUI. I am using the new refreshable api: Apple Developer Documentation. It is a bit annoying that they force you to use async await, since I have a part of refreshing code which can NOT be turned fully async await.

The reason I can not use async await for this code, is because the the application sends and receives messages over a websocket . The application manually matches the responses to the requests and than call the closure when they arrive. I don't believe this is possible to completely rewrite to async await.

I have a closure which eventually will always return a result. When that result have arrived, the spinner should go away. This is my horrible async await wrapper (simplified):

/// Terrible way of adding async await support
func sendAsync() async {
    try! await Task {
        var future: Void? = nil

        self.onReceive = { _ in
            future = ()
        }

        while true {
            if future != nil {
                return
            }

            // This is 10 ms
            try await Task.sleep(nanoseconds: 10_000_000)
        }
    }.value
}

So this is a spinlock based async await wrapper. I was wondering if there is a better way to do it. I wrapped in inside a Task , it won't run on the main thread that way.

Is there some existing async await wrapper which waits for a value to be non-nil and then wakes up? Is there a different api for refreshable which does not require async await?

This likely could be written with a continuation:

func sendAsync() async {
   return await withCheckedContinuation { continuation in
      self.onReceive { continuation.resume() }
   }
}
6 Likes

This is awesome!!!! Thanks a lot! :)