How to get results from asynchronous url request using swift?

I am trying to implement login functionality in an application. I have a url that takes username and password, checks if they are correct and returns true or false. If true, the new view controller should be loaded.

1) How do I get the result from an asynchronous url request in my viewController? The response from the server is correct and the json transfer is correct. However, I'm not sure how to get the result in the viewController. I have read other questions about Stack Overflow, some have mentioned using delegate and completeHandler. I've looked at how to use a delegate, but I'm not too sure how I can use it in this situation.

So here is my code. In one class, I have this function. I am accessing this functionality via a singleton. I call this in the shouldPerformSegueWithIdentifier method of the viewController (shown below).

func loginRequest() -> Bool {


    let urlPath: String = "http:**************.mybluemix.net/login/login.php?username=Tony&password=pass"
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSURLRequest = NSURLRequest(URL: url)
    let queue:NSOperationQueue = NSOperationQueue()
    var loginRequest:Bool = false
    NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var err: NSError
        var jsonResult: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil)
        println("AsSynchronous\(jsonResult)")

        if let jsonDictionary = jsonResult as? NSDictionary {
            if let success = jsonDictionary["Success"] as? NSString {
                if success == "true" {
                    // User has permission to login.
                    loginRequest = true
                    println("login should be true: \(loginRequest)")

                }else{
                    // User does not have permission to login.
                    loginRequest = false
                    println("login should be false: \(loginRequest)")
                }
            }

        }

    })
}

      

This function is in the view controller.

    override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
    if identifier == "SubmitLogin" {
        // Username empty.
        if(username.text.isEmpty){
            let alert = UIAlertView()
            alert.title = "no Text"
            alert.message = "No username or password"
            alert.addButtonWithTitle("OK")
            alert.show()
            return false
        }else{
            // Send login credentials to server.
            var login = (Model.sharedInstance.loginRequest())
            // loginRequest returns ture if credentials are correct.
            if (Model.sharedInstance.loginRequest()){
                // Start the next view controller.
                return true
            }else{
                // Login failed
                let alert = UIAlertView()
                alert.title = "Failed Login"
                alert.message = "The username or password was wrong. Please try again"
                alert.addButtonWithTitle("OK")
                alert.show()
                return false
            }

        }
    }
}

      

So how can I use a delegate with NSURLConnection.sendAsynchronousRequest to get the result in the viewcontroller and use the result to determine if I should be logged into the user?

****** Edit for clarification ******

I have a class called BluemixDAO that looks like this:

BluemixDAO class {

init(){
}

// The login function that I posted above
func loginRequest() -> Bool {
}
}

      

The next class is the model that is used to call the BluemixDAO login function.

class Model {
private let bluemix:BluemixDAO
private struct Static
{
static private var instance:Model?
}
class var sharedInstance: Model
{
if !(Static.instance != nil)
{
return Static.instance!
}
}

private init(){
bluemix = BluemixDAO()
}

// I call this function in my view controller using Model.sharedInstance.loginRequest()
func loginRequest() -> Bool 
{
// This calls the function in the BluemixDAO class
return bluemix.loginRequest()
}
}

      

The last class is viewController. It only has the above "shouldPerformSegueWithIdentifier" function

class viewController: UIViewController {

//This function is detailed at the top of the post.
    override func shouldPerformSegueWithIdentifier(identifier: String?, sender:AnyObject?) ->  Bool {
     // I call the loginRequest() function in here.
}
}

      

I have probably written too much. This should be all the relevant code. Greetings

+3


source to share


1 answer


It seems like you have your async block and your function in the same class, so the only thing you need to do is call your function again on the main thread, adding this code to the async block to do this:

dispatch_async(dispatch_get_main_queue())
{
        //call your function
}

      

You can also send a notification or send the result over the protocol if you want to send the result to another class.

I personally stop using UIAlertView for login since you cannot update it to show details like login failed or use to show spinning wheel while processing data among others, I prefer to create a new UIViewController and insert it into the screen to execute the whole process of login and dismiss.

Protocol use

To pass values ​​from one class to another, you need to implement protocols. In the class that logs in, it declares the protocol as follows:

protocol LoginDelegate
{
    func loginResult(result: Bool)
}

      

Declare an optional delegate as global in the same control:

var loginDelegate:LoginDelegate? = nil

      

Now check if this delegate exists before unpacking and if it calls the declare function in the protocol:

if let loginDel = self.loginDelegate{
    loginDel.loginResult(userResult)
}

      

this will help us pass the value to the class requesting it, in this class you need to extend this new protocol:

extension MainViewController: LoginDelegate{ }

In viewDidLoad declare this view controller as a delegate from the login class



login.loginDelegate = self

      

Now, to conform to the protocol, we just need to implement a function that will be called by the verbose controller with a boolean value:

extension LoginViewController: LoginDelegate
{
    func loginResult(result: Bool)
    {
       //Save the result value to be used by the MainViewController
    }
}

      

I know this is not a very easy process as after using it a few times it will be easier to use and understand, it is always good to read in the documentation here

Using alert

To use the notification add this code to send new notifications when the user completeness check process, the dictionary contains the data you want to pass in the notification, in this case the result of the login process:

dispatch_async(dispatch_get_main_queue())
{
    let dic = ["result":result]
    NSNotificationCenter.defaultCenter().postNotificationName("loginResult", object: nil, userInfo: dic)     
}

      

Register your class responsible for handling the message with this code:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "login:", name:"loginResult", object: nil)

      

Once the notification is received, the selector will call the function below, generate a notification to add the key to the dictionary:

func login(notification: NSNotification){
    if let result: AnyObject = notification.userInfo?["result"]
    {
        //do your logic
    }
}

      

Once you're done with the class , don't forget to unregister the notification or you'll have a memory leak

NSNotificationCenter.defaultCenter().removeObserver(self)

      

+3


source







All Articles