问题
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:
- a tableview view with a list of clients and a button to add new client.
- a detail view where I add/edit a client
- 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:
I tried some examples w semaphores... but it didn't work.
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