如何在 Swift 5.5 中将 async/await 与 SwiftUI 一起使用?

问题描述 投票:0回答:5

我一直在测试 Swift 5.5 版本中预览的异步/等待功能,但我无法从异步函数收集结果并使用 SwiftUI 显示它们。这是我的代码:

import SwiftUI

struct AsyncTestView: View {
    @State var text: String?

    // Async function
    func asyncGetText() async -> String {
        Thread.sleep(forTimeInterval: 10)
        return "My text"
    }
    
    // Stores the result of async function
    func fetchText() async {
        let text = await asyncGetText()
        DispatchQueue.main.async {
            self.text = text
        }
    }
    
    var body: some View {
        Text(text ?? "Loading")
            .onAppear(perform: fetchText)
    }
}

这会导致以下错误:

在不支持并发的函数中调用“异步”
将“async”添加到函数“fetchText()”以使其异步

async
添加到
fetchText()
函数会导致
.onAppear()
函数出现以下错误:

从“() async -> ()”类型的“异步”函数到同步函数类型“() -> Void”的转换无效

本文中,他们使用

@asyncHandler
标签来注释
fetchText
函数,但这会导致警告:
'@asyncHandler' has been removed from the language'

swift async-await swiftui swift5
5个回答
41
投票

我是您引用的文章的作者。

发现 SwiftUI 中的并发性中所述,视图可以利用新的

.task { }
.refreshable { }
修饰符来异步获取数据。

因此,您现在可以选择以下选项来调用异步代码:

func someSyncMethod() {
  doSomeSyncWork()
  Task {
    await methodThatIsAsync()
  }
}
List {
}
.task {
  await methodThatIsAsync()
}
List {
}
.refreshable {
  await methodThatIsAsync()
}

如果您使用单独的视图模型,请确保将其标记为

@MainActor
以确保在主要参与者上执行属性更新。

我更新了文章的代码:https://github.com/peterfriese/Swift-Async-Await-Experiments


34
投票

根据 WWDC 会议中的新信息 在 Swift 中认识 async/await WWDC21,23 分 28 秒,现在使用:

完成
Task {
   someState = await someAsyncFunction()
}

会议截图。

Image showing how to use an Async task on a sync context

请注意,实例化任务的方法有多种。这是最基本的。您也可以使用

Task.detached
,两种方式都可以获得优先级参数。 检查任务文档和会话 请在 23:05 左右查看探索 Swift WWDC21 中的结构化并发(我推荐整个会议!)以获取更多信息。


14
投票

我同意@peter-friese的答案,但会为那些阅读本文的人添加从同步桥接到异步时语法的变化:

新语法:

Task {
   someState = await someAsyncFunction()
}

替换此语法:

async {
   someState = await someAsyncFunction()
}

...并且

Task()
初始化器可以接受优先级参数:

Task(priority: .userInitiated) {
   someState = await someAsyncFunction()
}

3
投票
import SwiftUI

struct AsyncTestView: View {
    @State var text = "Loading"

    // Async function. Mark nonisolated if you want it on a background thread, e.g. if you have big synchronous loop.
    func asyncGetText() async -> String {
        try? await Task.sleep(nanoseconds: 3 * NSEC_PER_SEC)
        return "My text"
    }
    
    var body: some View {
        Text(text)
        .task {
             text = await asyncGetText()
        }
    }
}

0
投票

从 SwiftUI View 调用这个简化的

.task { }
版本

var body: some View {
     VStack(spacing:.default) {
          yourCustomView
        }
        .padding()
      }
      .task { await viewModel.onAppear() }
    }

在 ViewModel 中

func onAppear() async {
    await getStuffFromNetwork()
 }
© www.soinside.com 2019 - 2024. All rights reserved.