Reputation:
I am calling a method that executes a URLSession
but before it does anything, presents a UIAlertController
blocking the UI until some sort of response from the request is achieved. Logic tells me that dismissing the UIAlertController
in the completion block of that method where it is called on the main thread would be the best option. Am I wrong to assume this? Apparently so, as variably the presented UIAlertController
will indeed display, but never dismiss. Help?
Block:
getCostandIV { output in
let cost = output["ask"] as! NSNumber
let IV = output["IV"] as! NSNumber
self.enteredCost = cost.stringValue
self.enteredIV = IV.stringValue
DispatchQueue.main.async {
self.progress.dismiss(animated: true, completion: nil)
self.tableView.reloadSections(IndexSet(integer: 1), with: UITableView.RowAnimation.none)
self.canWeSave()
}
}
Function:
func getCostandIV (completionBlock: @escaping (NSMutableDictionary) -> Void) -> Void {
DispatchQueue.main.async {
self.progress = UIAlertController(title: "Retrieving ask price and volatility...", message: nil, preferredStyle: UIAlertController.Style.alert)
self.present(self.progress, animated: true, completion: nil)
}
guard let url = URL(string: "https://api.tdameritrade.com/v1/marketdata/chains?apikey=test&symbol=\(symbol)&contractType=\(type)&strike=\(selectedStrike)&fromDate=\(selectedExpiry)&toDate=\(selectedExpiry)") else {
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data,
error == nil else {
//print(error?.localizedDescription ?? "Response Error")
DispatchQueue.main.async {
self.presentedViewController?.dismiss(animated: true, completion: {
let alert = UIAlertController(title: "There was an error retrieving ask price and volatility.", message: "Please try again later.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alert, animated: true)
})
}
return }
do{
//here dataResponse received from a network request
let jsonResponse = try JSONSerialization.jsonObject(with:
dataResponse, options: [])
// //print(jsonResponse) //Response result
guard let jsonDict = jsonResponse as? NSDictionary else {
return
}
// //print(jsonDict)
var strikeMap : NSDictionary = [:]
if self.type == "CALL" {
strikeMap = jsonDict["callExpDateMap"] as! NSDictionary
} else {
strikeMap = jsonDict["putExpDateMap"] as! NSDictionary
}
self.strikes.removeAllObjects()
let inner = strikeMap.object(forKey: strikeMap.allKeys.first ?? "<#default value#>") as! NSDictionary
let innerAgain = inner.object(forKey: inner.allKeys.first ?? "<#default value#>") as! NSArray
let dict : NSDictionary = innerAgain[0] as! NSDictionary
let dict2 = ["ask" : dict["ask"] as! NSNumber, "IV" : dict["volatility"] as! NSNumber] as NSMutableDictionary
completionBlock(dict2)
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
Edit: Using self.presentedViewController?.dismiss(animated: true, completion: nil)
did not fix the issue. In addition, the completion block of the dismiss function for self.progress is not being called.
Edit 2: presentedViewController right before dismiss code in the callback is nil, even though present is called on the alert controller before dismiss?
Upvotes: 0
Views: 2193
Reputation: 261
Your alert would be dismissed only if everything goes well. I suggest you to change your function to something like this:
func getCostandIV (completionBlock: @escaping (NSMutableDictionary?, Error?) -> Void) -> Void
and make sure that your completionBlock
is called when your guard
statements fail or an error is thrown. In your current code the alert is only dismissed when it network request fails, but not when something goes wrong when parsing JSON.
Upvotes: 0
Reputation: 309
use this , for dismiss your alert you should add dismiss method in an async block and for setting timer for that you should tell async block to start being async from now to 5 seconds and after that do some thing :
alert.addAction(UIAlertAction(title: "ok", style: .default,
handler: nil))
viewController.present(alert, animated: true, completion: nil)
// change to desired number of seconds (in this case 5 seconds)
let when = DispatchTime.now() + 5
DispatchQueue.main.asyncAfter(deadline: when){
// your code with delay
alert.dismiss(animated: true, completion: nil)
}
Upvotes: 2
Reputation: 15748
If you call the getCostandIV
method more than once, the second alert won't be presented and self.progress
will have the reference of unpresented alert.
Change
self.progress.dismiss(animated: true, completion: nil)
To
self.presentedViewController?.dismiss(animated: true, completion: nil)
Upvotes: 0