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

How can I use async/await with SwiftUI in Swift 5.5?

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

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' call in a function that does not support concurrency
Add 'async' to function 'fetchText()' to make it asynchronous

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

Invalid conversion from 'async' function of type '() async -> ()' to synchronous function type '() -> Void'

this article 中,他们使用 @asyncHandler 标记来注释 fetchText 函数,但这会导致警告:'@asyncHandler' has been removed from the language'.

我是您引用的 the article 的作者。

Discover concurrency in 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

根据 WWDC 中的新信息 session 在 Swift 中与 async/await 会面 WWDC,在 23m:28s 现在使用:

async {
   someState = await someAsyncFunction()
}

来自 session 的屏幕截图。

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

新语法:

Task {
   someState = await someAsyncFunction()
}

替换此语法:

async {
   someState = await someAsyncFunction()
}

... 并且 Task() 初始化程序可以接受优先级参数:

Task(priority: .userInitiated) {
   someState = await someAsyncFunction()
}
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()
        }
    }
}