Wait Until Function Completes and Segue - swift

Swift is new for me and I stuck at this part. I want to request some data from api when I clicked the button. After that, want to pass the data i get from this function to another view controller. So basicly want to perform segue with this data. However, when I clicked the button, request starts but it directly perform segue to 2. view without any data.
How can I wait for:
videoManager.performRequest(with: videoLinkTextField.text!)
this function to perform segue?
This is my button:
#IBAction func getVideoButtonPressed(_ sender: UIButton) {
if videoLinkTextField.text != nil, videoLinkTextField.text!.contains("tiktok") {
videoManager.performRequest(with: videoLinkTextField.text!)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "GoToVideo", sender: self)
}
} else {
videoLinkTextField.text = ""
let alert = UIAlertController(title: "Error", message: "Please enter a valid link", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default))
present(alert, animated: true, completion: nil)
}
}
Here is my prepare function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "GoToVideo" {
DispatchQueue.main.async {
let destionationVC = segue.destination as! ResultViewController
print("Test \(self.videoUrl)")
destionationVC.videoUrl = self.videoUrl
}
}
}
And here is my performRequest function:
func performRequest(with videoUrl: String) {
let request = NSMutableURLRequest(url: NSURL(string: "https://tiktok-downloader-download-videos-without-watermark1.p.rapidapi.com/media-info/?link=\(videoUrl)")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { data, _, error in
if error != nil {
self.delegate?.didFailedWithError(error!)
}
if let safeData = data {
if let video = self.parseJSON(safeData) {
self.delegate?.didUpdateVideo(self, video: video)
}
}
})
dataTask.resume()
}

You are asking:
How can I wait for:
videoManager.performRequest(with: videoLinkTextField.text!)
Looks like you may want to use a closure for this method: Check out this article to learn more about closures:
Escaping Closures in Swift
However, I'll also provide you an example of how you'd might want your function to look:
Note, I only added , completion: #escaping () -> Void as a param and added a completion() to indicate where the closure should finish (You can also pass params in a completion block too (e.g: String, Error, etc)
func performRequest(with videoUrl: String, completion: #escaping () -> Void) {
let request = NSMutableURLRequest(url: NSURL(string: "https://tiktok-downloader-download-videos-without-watermark1.p.rapidapi.com/media-info/?link=\(videoUrl)")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { data, _, error in
if error != nil {
self.delegate?.didFailedWithError(error!)
}
if let safeData = data {
if let video = self.parseJSON(safeData) {
self.delegate?.didUpdateVideo(self, video: video)
}
}
completion() // finishes the closure
})
dataTask.resume()
}
Then you'll be able to do this:
videoManager.performRequest(with: videoLinkTextField.text!) { _ in
DispatchQueue.main.async {
self.performSegue(withIdentifier: "GoToVideo", sender: self)
}
}

Place segue here
if let video = self.parseJSON(safeData) {
self.delegate?.didUpdateVideo(self, video: video)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "GoToVideo", sender: self)
}
}

Related

CCavenue Payment Gateway with WKWebView - iOS (as UiWebview is deprecated since 2020 by apple)

Post December 2020, apple does not allow UiWebView support. ccavenue, being popular payment gateway in India, still have not updated their sdk from official website.
Here is the the complete code, to be replaced for UiWebView issue.
class CCWebViewController: BaseViewController, WKNavigationDelegate {
var mywebview: WKWebView!
var request: NSMutableURLRequest?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupWebView()
notification = NotificationCenter.default.addObserver(forName: .UIApplicationWillEnterForeground, object: nil, queue: .main) {
[unowned self] notification in
self.checkResponseUrl()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !isHere {
isHere = true
self.gettingRsaKey(){
(success, object) -> () in
DispatchQueue.main.sync {
if success {
self.encyptCardDetails(data: object as! Data)
}
else{
self.displayAlert(msg: object as! String)
}
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
LoadingOverlay.shared.showOverlay(view: self.view)
}
private func setupWebView(){
//setup webview
let config = WKWebViewConfiguration()
self.mywebview = WKWebView(frame: CGRect.init(x: self.view.bounds.origin.x, y: self.view.bounds.origin.y, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height), configuration: config)
mywebview.navigationDelegate = self
self.view.addSubview(mywebview)
}
private func gettingRsaKey(completion: #escaping (_ success: Bool, _ object: AnyObject?) -> ()){
DispatchQueue.main.async {
self.rsaKeyDataStr = "access_code=\(self.accessCode)&order_id=\(self.orderId)"
let requestData = self.rsaKeyDataStr.data(using: String.Encoding.utf8)
guard let urlFromString = URL(string: self.rsaKeyUrl) else{
return
}
var urlRequest = URLRequest(url: urlFromString)
urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
urlRequest.httpMethod = "POST"
urlRequest.httpBody = requestData
let session = URLSession(configuration: URLSessionConfiguration.default)
print("session",session)
session.dataTask(with: urlRequest as URLRequest) {
(data, response, error) -> Void in
if let response = response as? HTTPURLResponse, 200...299 ~= response.statusCode{
guard let responseData = data else{
print("No value for data")
completion(false, "Not proper data for RSA Key" as AnyObject?)
return
}
print("data :: ",responseData)
completion(true, responseData as AnyObject?)
}
else{
completion(false, "Unable to generate RSA Key please check" as AnyObject?) }
}.resume()
}
}
private func encyptCardDetails(data: Data){
guard let rsaKeytemp = String(bytes: data, encoding: String.Encoding.ascii) else{
print("No value for rsaKeyTemp")
return
}
rsaKey = rsaKeytemp
rsaKey = self.rsaKey.trimmingCharacters(in: CharacterSet.newlines)
rsaKey = "-----BEGIN PUBLIC KEY-----\n\(self.rsaKey)\n-----END PUBLIC KEY-----\n"
print("rsaKey :: ",rsaKey)
let myRequestString = "amount=\(amount)&currency=\(currency)"
do{
let encodedData = try RSAUtils.encryptWithRSAPublicKey(str: myRequestString, pubkeyBase64: rsaKey)
var encodedStr = encodedData?.base64EncodedString(options: [])
let validCharSet = CharacterSet(charactersIn: "!*'();:#&=+$,/?%#[]").inverted
encodedStr = encodedStr?.addingPercentEncoding(withAllowedCharacters: validCharSet)
CCWebViewController.statusCode = 0
//Preparing for webview call
if CCWebViewController.statusCode == 0{
CCWebViewController.statusCode = 1
let urlAsString = "https://secure.ccavenue.com/transaction/initTrans"
let encryptedStr = "merchant_id=\(merchantId)&order_id=\(orderId)&redirect_url=\(redirectUrl)&cancel_url=\(cancelUrl)&enc_val=\(encodedStr!)&access_code=\(accessCode)&billing_name=\(billingName)&billing_address=\(billingAddress)&billing_city=\(billingCity)&billing_state=\(billingState)&billing_country=\(billingCountry)&billing_tel=\(billingMobile)&billing_zip=\(pincode)&billing_email=\(billingEmail)&merchant_param1=\(notes)&billing_country=\(billingCountry)&merchant_param2=\(notes)&merchant_param3=\(notes)&merchant_param4=\(notes)&delivery_country=\(billingCountry)&delivery_cust_notes=\(notes)"
let myRequestData = encryptedStr.data(using: String.Encoding.utf8)
request = NSMutableURLRequest(url: URL(string: urlAsString)! as URL, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 30)
request?.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request?.setValue(urlAsString, forHTTPHeaderField: "Referer")
request?.httpMethod = "POST"
request?.httpBody = myRequestData
self.mywebview.load(request! as URLRequest)
}
else{
print("Unable to create requestURL")
displayAlert(msg: "Unable to create requestURL")
}
}
catch let err {
print(err)
}
}
func displayAlert(msg: String){
let alert: UIAlertController = UIAlertController(title: "ERROR", message: msg, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
LoadingOverlay.shared.hideOverlayView()
self.dismiss(animated: true, completion: nil)
}
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
//MARK: WebviewDelegate Methods
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.showToast(type: 0, message: "Error", timeToDisplay: 2)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print(String(describing: webView.url))
LoadingOverlay.shared.hideOverlayView()
webView.evaluateJavaScript("document.documentElement.outerHTML", completionHandler: { result, error in
if let datHtml = result as? String {
print(datHtml)
// parse datHtml here
let string = webView.url!.absoluteString
if(string.contains(self.redirectUrl))
{
print(self.mywebview.isLoading)
let html = datHtml
print("html :: ",html)
var transStatus = "Not Known"
if(html.contains("tracking_id"))
{
if(html.contains("Success"))
{
transStatus = "Transaction Successful"
let controller: CCResultViewController = CCResultViewController()
controller.transStatus = transStatus
controller.isSucceed = true
self.present(controller, animated: true, completion: nil)
}
else if(html.contains("Aborted"))
{
transStatus = "Transaction Cancelled"
let controller: CCResultViewController = CCResultViewController()
controller.transStatus = transStatus
controller.isSucceed = false
self.present(controller, animated: true, completion: nil)
}
else
{
transStatus = "Transaction Failed"
let controller: CCResultViewController = CCResultViewController()
controller.transStatus = transStatus
controller.isSucceed = false
self.present(controller, animated: true, completion: nil)
}
}
else{
print("html does not contain any related data")
self.displayAlert(msg: "html does not contain any related data for this transaction.")
}
}
}
} )
}
}

Authentication error/ purple warning when trying to segue after validating data from API

I have created a POST request which validates if the username and password are correct through a StatusCode: 0 that comes from the response of the POST request if the data are correct, At the signInViewController class I have created the button signInSegueToDashboard which when pressed must validate the data and if the data are valid then the user will be logged in without any problem.
The button sender at signInViewController:
#IBAction func signInSegueToDashboard(_ sender: Any) {
APICallerPOST.shared.signInToAccount(username: emailTextField.text!, password: passwordTextField.text!) { (result, error) in
if let result = result {
if result.StatusCode == 0 {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") else {
return
}
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) {_ in
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}else if result.StatusCode == 5 {
print("error: \(error!.localizedDescription)")
}
}
}
}
When i press the button after typing the correct data it just does nothing and just shows a purple warning that is saying to put it on Main thread, When i did put on main thread the segue part then it doesn't validate the data at all instead it just logs you in without any validation.
the POST request from APICallerPOST class:
func signInToAccount(username: String, password: String, completion: #escaping (SignInResponse?, Error?) -> Void) {
//declare parameter as a dictionary which contains string as key and value combination.
let parameters = ["User": username, "Password": password]
//create the url with NSURL
let url = URL(string: "https://censoredurl/Signin")!
//create the session object
let session = URLSession.shared
//now create the Request object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to data object and set it as request body
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
//HTTP Headers
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError(domain: "dataNilError", code: -100001, userInfo: nil))
return
}
do {
//create json object from data
let decoder = JSONDecoder()
guard let json = try? decoder.decode(SignInResponse.self, from: data) else {
completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
return
}
print(json)
completion(json, nil)
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
})
task.resume()
}
Confused a lot.
The dataTask is asynchronous, and so is the code it runs in the completion handler. However all updates to UI need to be performed on the main thread, and so the parts of the completion handler that update the UI need to be pushed back onto the main thread.
In your case you could do it like this:
if result.StatusCode == 0 {
DispatchQueue.main.async {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController")
else {
return
}
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) {_ in
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}
// ...
However it seems you are trying to use the Timer to delay presentation of the viewController, and there is a better way of doing this than using the Timer. You can use a delayed execution with DispatchQueue's asyncAfter(deadline:qos:flags:execute:) method:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// do your UI work
}

Where do Dispatch Group commands go in code?

I am trying to run a function X times in a for in loop but when all the functions have returned I want to run another function.
Currently I have it working by delaying the final function 1 second but I would really like to get Dispatch Group working.
I've been through various online examples and other questions but nothing I try seems to work, The code I have at the moment I know won't work as it is running dispatchGroup.leave() each time the for in functions are sent rather than when they return.
I've tried puting the DispatchGroup code in the function (which is in another file) but I'm stumped, however I think I am close to a solution.
I've also looked at semaphores and using count and incrementing a value each time the loop runs but I keep coming back to DispatchGroups.
My last resort is to ask a question!
ViewController code
#IBAction func removeDeviceBtn(_ sender: Any) {
let dispatchGroup = DispatchGroup()
for owner in arrOwnerList {
dispatchGroup.enter()
self.removeDevice(device: self.device, account: owner as! String, completion: self.completed)
dispatchGroup.leave()
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
self.removeDeviceFromServer(device: self.device)
self.sendEmail(to:"gordon#example.co.uk", subject:self.device+" has been removed", text:self.device+" has been removed from the server, please check the sim for bar and termination")
})
Function code in other file as an extension
func completed(isSuccess: Bool) {
}
func removeDevice(device: String, account: String, completion: #escaping (Bool) -> Void) {
let dictHeader : [String:String] = ["username":Username,"password":Password]
let dictArray = [device]
WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { (response) in
if response.count == 0 {
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
}
}
else {
if response.count != 0 {
let isSuccess = true
completion(isSuccess)
}
else{
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.NoDataFound, on: self)
}
}
}
}) { (error) in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
}
}
}
Code from WebHelper file
class func requestPUTAPIRemoveDevice(_ strURL: String,header: Dictionary<String,String>,dictArray: Array<Any>, controllerView viewController: UIViewController, success: #escaping (_ response: [AnyHashable: Any]) -> Void, failure: #escaping (_ error: Error?) -> Void) {
if GlobalConstant.isReachable() {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.showHUD()
}
let loginString = String(format: "%#:%#", header["username"]!, header["password"]!)
let loginData: Data = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString(options: NSData.Base64EncodingOptions())
let headers = ["Authorization": "Basic "+base64LoginString, "Referer": "http://www.example.com"]
let postData = try? JSONSerialization.data(withJSONObject: dictArray, options: [])
let request = NSMutableURLRequest(url: NSURL(string: strURL)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
failure(error)
} else {
if let httpResponse = response as? HTTPURLResponse {
print("Server code \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 || httpResponse.statusCode == 208 {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
if (jsonResult is NSDictionary) {
success(jsonResult as! [AnyHashable : Any])
}
else if (jsonResult is NSArray) {
success(["response":jsonResult as! NSArray])
}
else{
success(["response":httpResponse.statusCode])
DispatchQueue.main.async {
}
}
}
else{
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
failure(error)
}
}
}
})
dataTask.resume()
}
else {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
GlobalConstant.showAlertMessage(withOkButtonAndTitle: "", andMessage: "Internet not connected", on: viewController)
}
}
}
The final solution (apart from tidying up the various other issues) was to add success(["response":httpResponse.statusCode]) to the WebHelper file, corrected code above
Put the leave inside the completion handler:
for owner in arrOwnerList {
dispatchGroup.enter()
removeDevice(device: device, account: owner as! String) { [weak self] success in
self?.completed(isSuccess: success)
dispatchGroup.leave()
}
}
Or given that you’re not really doing anything in completed function, I’d just remove that:
for owner in arrOwnerList {
dispatchGroup.enter()
removeDevice(device: device, account: owner as! String) { _ in
dispatchGroup.leave()
}
}
I notice that you have paths of execution in removeDevice that aren’t calling the completion handler. Make sure every path of execution calls the completion handler or else your dispatch group will never get resolved.
func removeDevice(device: String, account: String, completion: #escaping (Bool) -> Void) {
let dictHeader = ["username": Username, "password": Password]
let dictArray = [device]
WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { response in
DispatchQueue.main.async {
if response.count == 0 {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
completion(false)
} else {
completion(true)
}
}
}, failure: { error in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
completion(false)
}
})
}
By the way, I don’t know the name of the “failure” closure, so I assumed it was failure, but adjust as required by your requestPUTAPIRemoveDevice method. We generally avoid the multiple closure pattern in Swift, but if you’re going to do that, I’d avoid the trailing closure syntax. It makes the functional intent of this second closure a bit more explicit.
Or, this all begs the question as to why requestPUTAPIRemoveDevice is initiating UI updates at all. I’d probably put that in the view controller method. So requestPUTAPIRemoveDevice should just pass back enough information so the removeDeviceBtn routines knows what error to present. And this idea of presenting a separate error message for each failure is probably suspect, too. (E.g. if you have lost internet connection and are trying to remove a dozen devices, do you really want to show a dozen separate error messages?) But this is beyond the scope of this question.

completion handler can not dismiss the popAlertController

I have a view controller with a single textView which I get a string from server and set on it.
I call a http-get service in viewDidAppear and I need to show a popUp alert for waiting user till get the string
I do all but I can not dismiss the popalert and app stacks in loading alert
override func viewWillAppear(_ animated: Bool) {
fetchAndPrintEachPerson()
networkReachablity()
popUpLoading() // start showing loading pop alert
getPerson ()
}
override func viewDidLoad() {
super.viewDidLoad()
style()
giftTextFiled.isEnabled = false
}
func popUpLoading(){
popUpAlert = UIAlertController(title: nil, message: "wait ...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
popUpAlert.view.addSubview(loadingIndicator)
present(popUpAlert, animated: true, completion: nil)
}
func getPerson(){
guard let url=URL(string: "\(address)person/code") else {return}
print(url)
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let res=response {
//print(res)
}
do {
if let dataContent=data {
let con = try? JSONSerialization.jsonObject(with: dataContent, options: []) as? AnyObject
let data = con??["data"] as? AnyObject
let code = data?["code"] as? String
print(code)
// self.popUpAlert.dismiss(animated: true, completion: {
DispatchQueue.main.async(execute: {
//self.giftTextFiled.text=code!
self.giftTextFiled.isEnabled = true
self.popUpAlert.dismiss(animated: true, completion: nil)
})
//})
}else {
let snack=snackBarAlert()
snack.alert(title: "error", color: UIColor.red)
self.popUpAlert.dismiss(animated: true, completion: nil)
}
}catch let err{
print(err)
DispatchQueue.main.async(execute: {
self.popUpAlert.dismiss(animated: true, completion: nil)
})
}
}.resume()
}
update first post.
Try putting that inside view DidLoad & check whether your getting a response from the server other wise it will fall through else block and keep showing the pop up
URLSession.shared.dataTask completion closure is called on non-main thread, which may produce unexpected results for UI. Dispatch your UI code to main thread.
URLSession.shared.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
// Your UI related code
}
}.resume()

UIAlertView in closures callback function in Swift

I need to login through api when user click login button. The return results might failure or success. I want to use alterview to notice the user.
I defined one global variable in class:
var responseString:String = "";
In button click event:
#IBAction func login(sender: AnyObject) {
sendPostLoginRequest {
results in
println("here:\(results)")
self.responseString = results
}
var myAlert = UIAlertController(title:"Alert", message:self.responseString, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.Default, handler:nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated:true, completion:nil);
}
The self.responseString is empty, even through i can get it in sendPostLoginRequest function:
func sendPostLoginRequest(completionHandler:(result:String)->()){
let name = usernamefield.text
let password = passwordfield.text
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.xxx.xxx/api/user/login")!)
request.HTTPMethod = "POST"
let postString = "username=\(name)&password=\(password)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
}
let json = JSON(data: data)
println("results: \(json) ")
self.responseString = json[0].string!
//json[0].string! can be success or failure
completionHandler(result:self.responseString)
}
task.resume()
}
The way I see it, I think when you click the button, the alertView is shown without the responseString or nil. The problem is you are anticipating the block will get called later on and hence the data for responseString will be set later in time but the code below will continue to execute.
The long way to do is:
//observe this variable and when something changes we will alert it
var responseString:String {
didSet{
//do some checking or other work
presentAlert(responseString)
}
}
#IBAction func login(sender: AnyObject) {
sendPostLoginRequest {
results in
println("here:\(results)")
self.responseString = results
}
}
func presentAlert(msg:String){
var myAlert = UIAlertController(title:"Alert", message:msg, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.Default, handler:nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated:true, completion:nil);
}
The short way to do is:
#IBAction func login(sender: AnyObject) {
sendPostLoginRequest {
results in
println("here:\(results)")
self.responseString = results
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//update ui from main thread always
self.presentAlert(results) //may pass responseString too
})
}
}
func presentAlert(msg:String){
var myAlert = UIAlertController(title:"Alert", message:msg, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.Default, handler:nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated:true, completion:nil);
}
Had you put the code below the closure inside it would have worked but you would be running UI things from other threads which are bad so use Dispatch Async to work for long-running operations on a separate thread.
Let me know if that helps and hope it does. Cheers!