user3210341
user3210341

Reputation: 77

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()


}

Upvotes: 0

Views: 2862

Answers (1)

kandelvijaya
kandelvijaya

Reputation: 1585

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!

Upvotes: 1

Related Questions