如何在视图中显示 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
我有一个 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