[英]Why is Swift @escaping closure not working?
I'm trying to show a list of Customers when the app(iOS) main view loads.我试图在应用程序(iOS)主视图加载时显示客户列表。 The data is returned from an API request in JSON format which I confirmed I'm getting in the debugger.
数据是从一个 JSON 格式的 API 请求返回的,我确认我正在调试器中获取它。 I'm having issues getting the response from the main view because the fetch method is getting called after the view load method finished.
我在从主视图获取响应时遇到问题,因为在视图加载方法完成后调用了 fetch 方法。 As I read, I should add a completion handler to the fetch method and pass it as an escaping enclosure so the main method waits for the fetch method.
在我阅读时,我应该向 fetch 方法添加一个完成处理程序并将其作为转义外壳传递,以便 main 方法等待 fetch 方法。 I follow this guide and read it several times and still can't get where the error is.
我按照本指南阅读了几次,但仍然无法找到错误所在。 What am I missing?
我错过了什么?
This这个
struct CustomerList: View {
@State var customerList = searchCustomer(criteria: "jose")
var body: some View {
List(self.customerList, id: \.id){ customer in
CellRow(customer: customer)
}
}
}
func searchCustomer(criteria: String) -> [Customer] {
//API call full url
let fullURL = FTP_API_URL + criteria
var customerList = [Customer]()
if criteria == ""{
return customerList
}
getJsonFromAPI(url: fullURL, fetchCompletionHandler: {customers, error in
if let jsonCustomers = customers{
customerList = jsonCustomers.customers
}
})
return customerList
}
func getJsonFromAPI(url: String, fetchCompletionHandler : @escaping (Customers?, Error?)-> Void){
let username = decryptData(data: FTP_API_USERNAME)
let password = decryptData(data: FTP_API_PASSWORD)
let loginData = String(format: "%@:%@", username, password).data(using: String.Encoding.utf8)!
let base64LoginData = loginData.base64EncodedString()
// create the request
let url = URL(string: url)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Basic \(base64LoginData)", forHTTPHeaderField: "Authorization")
//making the request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
//if let httpStatus = response as? HTTPURLResponse {
// check status code returned by the http server
//print("status code = \(httpStatus.statusCode)")
// process result
// }
let jsonDecoder = JSONDecoder()
do{
let results = try jsonDecoder.decode(Customers.self, from: data)
// for result in results.customers{
// print(result.cedulaOrPassport)
// }
fetchCompletionHandler(results, nil)
}
catch{
print(error)
fetchCompletionHandler(nil, error)
}
}
task.resume()
}
Thanks in advance.提前致谢。
return customerList
in searchCustomer
happens synchronously when the data (that's obtained asynchronously from getJsonFromAPI
) isn't yet available.当数据(从
getJsonFromAPI
异步getJsonFromAPI
)尚不可用时, searchCustomer
return customerList
同步发生。 So, you're assigning and empty [Customer]
array to @State var customerList
.因此,您正在将
[Customer]
数组分配给@State var customerList
并将其清空。
In any case, you can't directly assign an asynchronously-obtained value to a property.在任何情况下,您都不能直接将异步获取的值分配给属性。
Instead, change searchCustomer
to also accept a completion handler, and use .onAppear
to invoke it and assign the value from within a completion handler (just like you with getJsonFromAPI
):相反,将
searchCustomer
更改为也接受完成处理程序,并使用.onAppear
调用它并从完成处理程序中分配值(就像使用getJsonFromAPI
):
func searchCustomer(criteria: String,
completionHandler: @escaping ([Customer]) -> Void) -> Void {
//API call full url
let fullURL = FTP_API_URL + criteria
var customerList = [Customer]()
if criteria == "" {
completionHandler(customerList)
}
getJsonFromAPI(url: fullURL, fetchCompletionHandler: {customers, error in
if let jsonCustomers = customers{
customerList = jsonCustomers.customers
completionHandler(customerList)
}
})
}
struct CustomerList: View {
@State var customerList = []
var body: some View {
List(self.customerList, id: \.id){ customer in
CellRow(customer: customer)
}
.onAppear() {
searchCustomer(criteria: "jose") { customers in
customerList = customers
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.