How to adapt RunLoop to swift concurrency(async/await)

I'm having some compilation warnings using Xcode 14.2/swift 5.7 (future errors in swift 6).

I've some async function tests in a unit test target which include some code to process UI changes in the main loop.

There are two related warnings:

RunLoop.current.run(until: Date())
// which raises next warning
Class property 'current' is unavailable from asynchronous contexts; currentRunLoop cannot be used from async contexts.; this is an error in Swift 6
CFRunLoopRunInMode(CFRunLoopMode.defaultMode, 0.1, false)
// which raises next warning
Global function 'CFRunLoopRunInMode' is unavailable from asynchronous contexts; CFRunLoopRunInMode cannot be used from async contexts.; this is an error in Swift 6

Here a full function test example.

    @MainActor
    func testBasic() async throws {
        // GIVEN
        sut = MainViewController(nibName: nil, bundle: nil)
        present(viewController: sut)

        // WHEN
        sut.loadViewIfNeeded()
        sut.showLoading()
        RunLoop.current.run(until: Date())

        sut.hideLoading()
        sut.showNoConnection()
        RunLoop.current.run(until: Date())

        // THEN
        XCTAssertTrue(sut.connectionStatus.isHidden == false)
    }

What alternatives would you use at this point to fix the warnings?

Post not yet marked as solved Up vote post of kikeenrique Down vote post of kikeenrique
2.3k views

Replies

What alternatives would you use at this point to fix the warnings?

I would change testBasic() to not be an async function.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for your answer @eskimo, but I was looking forward to find a fix for the warning, which I haven't found it exists at the moment.

So far, my understanding is that Runloop methods have been declared not async safe (which is understandable), but without adding an async alternative so far.

So, at the moment, there are no fixes to this warnings, and instead it would be needed to avoid using Runloops in async contexts.

I was looking forward to find a fix for the warning

I think you’ve misunderstood what the warning is telling you. You can’t mix run loop based code and Swift concurrency code willy-nilly. A run loop is an event source dispatching mechanism. When you run it, it expects to block the calling thread for arbitrary amounts of time. If you call it from an async function, the calling thread is one of the threads from Swift concurrency’s cooperative thread pool. Blocking such a thread for an arbitrary amount of time is a very bad idea.

I’m going to suggest two bits of ‘homework’ here:

If, after working through those resources, you still have questions, feel free to post a follow up here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the links and sorry to haven't followed up quicker.

I think I've not been able to explain what I expect to achieve (or even if it ever will be possible), I hope this time I will.

I understand that swift concurrency actors/Tasks are not in the same conceptual world than Runloops/Threads.

What I would expect to be able to do is to replace the Runloop code with something equivalent to

RunLoop.current.run(until: Date())

but in Tasks/Actors world.

await MainActor.run(forInterval: 0.1)

Or even better, what I would really like to achieve is to replace that piece of code (that Runloop snippet that I've copycat from some people unit tests code) that tries to force UI events processing.