如何在视图中显示 Swift 中完成处理程序的返回值?

How to display in a View a returned value from a completion handler in Swift?

我有一个 class 有这两种方法:

private func send(method: String, path: String, code: Array<Int>, headers: HTTPHeaders, completionHandler: @escaping (Int) -> Void) {
        let url: String = "\(self.credentials.url)/\(path)"

        AF.request(url, method: HTTPMethod(rawValue: method), headers: headers)
            .authenticate(with: self.request_credentials)
            .response { response in
                let status_code: Int = response.response!.statusCode
                
                completionHandler(status_code as Int)
            }
}

func list_files(path: String) {
        let headers: HTTPHeaders = [
            "Depth": "1"
        ]
        
        send(method: "PROPFIND", path: path, code: [207, 301], headers: headers) { status_code in
            self.status_code = String(status_code)
        }
}

所以这两个函数都使用了完成处理程序,因为 Alamofire 在其发出 HTTP 请求的过程中使用了它们。
我明白我必须以这种方式使用完成处理程序来处理这个异步数据。

我当前的问题是现在我必须在视图中显示此数据(比方说 self.status_code),但我不知道如何执行此操作。当我这样显示时:

struct ContentView: View {
    var body: some View {
        
        let auth = Authentication(username: "****",
                                  domain: "****",
                                  password: "****",
                                  port: ****,
                                  proto: "****",
                                  path:"****")
        
        let commands = Commands(credentials: auth.get_credentials())
        
        let _ = commands.list_files(path: "/")
        
        Text(commands.status_code)
            .padding()
    }
}

它将显示status_code的初始化值(即0)而不是list_files方法更新的值。我知道这是因为这些完成处理程序的异步行为,当我显示它时该值尚未更新。

但问题是:我怎样才能正确地向用户显示这个更新后的值?

我可能在这里做错了很多事情,我不介意收到一个完全不同的解决方案,因为我愿意遵循最佳实践。

谢谢。

下面是带有完整示例的游乐场。我将介绍一些需要注意的重要事项。

首先,我简化了“发送”方法,因为我没有原始示例中的所有类型和内容。此发送将等待 3 秒,然后使用您提供的任何消息调用完成处理程序。

在视图中,按下按钮时,我们调用“发送”。然后,在完成处理程序中,您会注意到:

DispatchQueue.main.async {
    message = msg
}

我不知道发送中的 Timer 将使用哪个线程来调用我的完成处理程序。但是 UI 更新需要在主线程上发生,因此 DispatchQueue.main... 构造将确保 UI 更新(将 message 设置为 msg)将发生在主线程。

import UIKit
import SwiftUI
import PlaygroundSupport

func send(message: String, completionHandler: @escaping (String) -> Void) {
    Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { timer in
        completionHandler(message)
        timer.invalidate()
    }
}

struct ContentView: View {
    @State var message : String = ""

    var body: some View {
        print("Building view")
        return VStack {
            TextField("blah", text: $message)
            Button("Push Me", action: {
                send(message: "Message Received") { msg in
                    DispatchQueue.main.async {
                        message = msg
                    }
                }
            })
        }.frame(width: 320, height: 480, alignment: .center)
    }
}


let myView = ContentView()
let host = UIHostingController(rootView: myView)
PlaygroundSupport.PlaygroundPage.current.liveView = host