简体   繁体   English

为什么 Swift @escaping 闭包不起作用?

[英]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这个

  1. This is the SwiftUI view where I'm calling the search method:这是我调用搜索方法的 SwiftUI 视图:
    struct CustomerList: View {
    
    @State var customerList = searchCustomer(criteria: "jose")
    
    
    
    var body: some View {
        List(self.customerList, id: \.id){ customer in
            CellRow(customer: customer)
        
        }
     }
    }
  1. This is the search function(Is actually a function, not part of any class or struct) called in the view load event这是视图加载事件中调用的搜索函数(实际上是一个函数,不是任何类或结构的一部分)
    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
  }
  1. The fetch async function(Is actually a function, not part of any class or struct) fetch async 函数(实际上是一个函数,不属于任何类或结构体)
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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM