Wait for task return on session.dataTask

北城以北 提交于 2019-12-24 18:28:22

问题


I have a project where I send and get data back from an API. Sending and getting it works fine.

My goal is to wait a response (ie: yes|no) from the server and depending on it, proceed with the segue or not and show an alert if no.

For this part I have:

  1. a tableview view with a list of clients and a button to add new client.
  2. a detail view where I add/edit a client
  3. on detail view there is a save button with a segue to the tableview view

The function saveClient() gets the data and makes a request to the server

func saveClient() -> Bool {

    let name = txtName.text ?? ""
    let address = txtAddress.text ?? ""
    let city = txtCity.text ?? ""
    let province = txtProvince.text ?? ""
    let postal_code = txtPostalCode.text ?? ""

    meal = Client(name:name, client_id: "", postal_code: postal_code, province: province, city: city, address: address)

    var jsonData = Data()
    let jsonEncoder = JSONEncoder()
    do {
        jsonData = try jsonEncoder.encode(meal)
    }
    catch {
    }
    print("a1")
    var success: Bool
    success = false

    makeRequestPost(endpoint: "http://blog.local:4711/api/clients/add",
                    requestType: "POST",
                    requestBody: jsonData,
                    completionHandler: { (response : ApiContainer<Client>?, error : Error?) in
                        if let error = error {
                            print("error calling POST on /todos")
                            print(error)
                            return
                        }
                        let b = (response?.meta)!
                        print(b.sucess)
                        if(b.sucess == "yes") {
                            success = true
                        }
                        else
                        {
                            DispatchQueue.main.async(execute: {
                                let myAlert = UIAlertController(title: "Error", message: "Error creating Client", preferredStyle: .alert)
                                let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
                                myAlert.addAction(okAction)
                                self.present(myAlert, animated: true, completion: nil)
                            })
                            return
                        }
    } )
    return success
}

on this same controller:

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {

    guard let button = sender as? UIBarButtonItem, button === btnSave else {
        return false
    }

    if !saveClient() {
        print("no sir")
        return false
    }

    print("yes sir")
    return true
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)        
    guard let button = sender as? UIBarButtonItem, button === btnSave else {
        return
    }
}

request function:

func makeRequestPost<T>(endpoint: String,
                     requestType: String = "GET",
                     requestBody: Data,  completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) {

    guard let url = URL(string: endpoint) else {
        print("Error: cannot create URL")
        let error = BackendError.urlError(reason: "Could not create URL")
        completionHandler(nil, error)
        return
    }

    var urlRequest = URLRequest(url: url)
    let session = URLSession.shared

    urlRequest.httpMethod = "POST"
    urlRequest.httpBody = requestBody

    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")



    let task = session.dataTask(with: urlRequest, completionHandler: {
        (data, response, error) in
        guard let responseData = data else {
            print("Error: did not receive data")
            completionHandler(nil, error)
            return
        }
        guard error == nil else {
            completionHandler(nil, error!)
            return
        }

        do {
            let response = try JSONDecoder().decode(ApiContainer<T>.self, from: responseData)
            completionHandler(response, nil)
        }
        catch {
            print("error trying to convert data to JSON")
            print(error)
            completionHandler(nil, error)
        }

    })
    task.resume()
}

On the console i get:

a1

no sir

yes

The correct would be:

a1

yes

yes sir

Final notes:

  1. I tried some examples w semaphores... but it didn't work.

  2. I'm using a var named meal on other places and haven't changed it to clients. However, it doesn't interfere on that part of the code


回答1:


You said:

My goal is to wait a response (ie: yes|no) from the server and depending on it, proceed with the segue or not

While I understand the natural appeal of that approach, the reality is that you never should "wait" (especially in shouldPerformSegue). Your UI will freeze (especially notable if the user has poor Internet connectivity), your app is susceptible to being killed by the watchdog process that looks for frozen apps, etc.

So, rather than performing the segue and having shouldPerformSegue try to wait for the network response to see if it's OK to proceed, do it the other way around: You can write code that performs the query and if everything is good, only then initiate segue programmatically.



来源:https://stackoverflow.com/questions/48100651/wait-for-task-return-on-session-datatask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!