我该如何在Swift 5.5中使用async/await和SwiftUI?

32

我一直在测试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' 调用
将 'async' 添加到函数 'fetchText()' 以使其异步

为了使 fetchText() 函数异步化,需添加 async 关键字。但是,这样更改后,.onAppear() 函数将出现以下错误:

无效转换,从类型为 '() async -> ()' 的 'async' 函数转换为同步函数类型 '() -> Void'

该文章中,作者使用 @asyncHandler 标签来注释 fetchText 函数,但这将导致警告:'@asyncHandler' 已从语言中删除


1
Xcode 12.5是Swift 5.4,所以我们正在谈论什么?你安装了不同的工具链吗? - matt
3
是的,我已经下载了Swift 5.5开发快照工具包。 - mistry
5个回答

38

我是您所提到的文章的作者。

正如在发现SwiftUI中的并发性中讨论的那样,视图可以使用新的.task { }.refreshable { }修饰符异步获取数据。

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

func someSyncMethod() {
  doSomeSyncWork()
  Task {
    await methodThatIsAsync()
  }
}

List {
}
.task {
  await methodThatIsAsync()
}

List {
}
.refreshable {
  await methodThatIsAsync()
}

如果您正在使用单独的视图模型,请确保将其标记为@MainActor,以确保属性更新在主actor上执行。
我已经更新了我的文章代码: https://github.com/peterfriese/Swift-Async-Await-Experiments

4
我曾经亲身经历过这种做法的真实代码(比如相互依赖的连续在线数据库调用)。最终,组合框架救了我,但异步/等待真的会拯救我。事实上,95%的程序员似乎甚至不理解异步是什么意思(因此有了我的http://www.programmingios.net/what-asynchronous-means/),而有了异步/等待,他们就不必了! - matt
1
@matt 我在这里只有一年左右,但四分之一的问题都是因为完成处理程序。期待async/await的到来! - aheze

31
根据WWDC21中遇见Swift中的异步/等待会话中的最新信息,在23分28秒时,现在使用以下方法实现:
Task {
   someState = await someAsyncFunction()
}

会话截图。

图像显示如何在同步上下文中使用异步任务

请注意,有更多实例化任务的方法。这是最基本的。您也可以使用Task.detached,两种方式都可以获得优先级参数。 查看Task文档和会话 有关更多信息,请查看大约在23:05的在Swift WWDC21中探索结构化并发(我建议观看整个会话!)。


我相信你不会。这是iOS15+的API。但奇怪的是,目前Xcode Beta 1让我部署到我的iPhone上,它运行的是iOS14.5。但是一旦调用了一些Async/Await代码,它就会崩溃。 - Nuno Gonçalves
我不记得在哪里读到过可以通过Actor实现。 - Victor Kushnerov
我已经阅读了Xcode beta 2的发布说明。苹果认为这是一个问题,而不是功能。已知问题 Swift并发性需要部署目标为macOS 12、iOS 15、tvOS 15和watchOS 8或更新版本。(70738378) - Victor Kushnerov
1
已弃用(使用Xcode 13.2.1):'async(priority:operation:)'已弃用:'async'已被'Task.init'替换,并将很快被删除。 - smat88dd
还可以查看下面的亚当答案 https://dev59.com/r1EG5IYBdhLWcg3wW7M5#69791347 - smat88dd
显示剩余3条评论

14

我同意@peter-friese的答案,但是要为阅读本文的人添加一些内容,即在从同步切换到异步时需要改变语法:

新语法:

Task {
   someState = await someAsyncFunction()
}

替换这个语法:

async {
   someState = await someAsyncFunction()
}

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

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

2
import SwiftUI

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

    // Async function
    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视图中调用这个简化的.task { }版本

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

而在 ViewModel 中

func onAppear() async {
    await getStuffFromNetwork()
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接