如何适配RunLoop至Swift并发(async/await)?解决编译警告
替代RunLoop的异步测试UI刷新方案
方案1:使用await Task.yield()
Task.yield()会让当前异步任务暂停,让出主线程处理所有待执行的UI更新和主队列任务,是最贴合Swift Actors/Tasks体系的替代方式。
修改后的测试代码:
@MainActor func testBasic() async throws { // GIVEN sut = MainViewController(nibName: nil, bundle: nil) present(viewController: sut) // WHEN sut.loadViewIfNeeded() sut.showLoading() await Task.yield() // 替代RunLoop.run,等待UI更新完成 sut.hideLoading() sut.showNoConnection() await Task.yield() // 等待UI状态变更 // THEN XCTAssertFalse(sut.connectionStatus.isHidden) }
方案2:等待指定时长确保UI处理完成
如果UI更新涉及动画或延迟操作,Task.yield()可能无法满足需求,可结合Task.sleep(nanoseconds:)等待固定时长,确保UI渲染完成:
@MainActor func testBasic() async throws { // GIVEN sut = MainViewController(nibName: nil, bundle: nil) present(viewController: sut) // WHEN sut.loadViewIfNeeded() sut.showLoading() // 等待0.1秒,确保UI渲染完成 try await Task.sleep(nanoseconds: 100_000_000) await Task.yield() sut.hideLoading() sut.showNoConnection() try await Task.sleep(nanoseconds: 100_000_000) await Task.yield() // THEN XCTAssertFalse(sut.connectionStatus.isHidden) }
方案3:自定义等待UI状态的工具函数
如果需要等待特定UI状态变为预期值,可编写异步轮询函数,避免固定等待时长的不确定性:
@MainActor func waitUntil(_ condition: @escaping () -> Bool, timeout: TimeInterval = 1) async throws { let startTime = Date() while !condition() { if Date().timeIntervalSince(startTime) > timeout { throw XCTestError("等待UI状态超时") } await Task.yield() } } // 测试中使用 @MainActor func testBasic() async throws { // GIVEN sut = MainViewController(nibName: nil, bundle: nil) present(viewController: sut) // WHEN sut.loadViewIfNeeded() sut.showLoading() await waitUntil { sut.loadingIndicator.isHidden == false } sut.hideLoading() sut.showNoConnection() await waitUntil { sut.connectionStatus.isHidden == false } // THEN XCTAssertFalse(sut.connectionStatus.isHidden) }
关键说明
- 所有方案均在
@MainActor上下文执行,确保UI操作和状态检查都在主线程 Task.yield()是Swift异步体系中让出执行权的标准方式,可替代RunLoop手动运行逻辑,适配Swift 6的异步上下文规则- 避免使用RunLoop/CFRunLoop相关API,消除编译警告并兼容未来Swift版本
内容的提问来源于stack exchange,提问作者kikeenrique
相关产品推荐
相关产品推荐

