1

I have the following code which performs a login to a web server, retrieves an API token, and then allows the app to continue. After this code executes, the the calling function uses the login_result to report to the user whether they successfully logged in or need to try again.

Right now, I just have the app sleep for 3 seconds to make sure I have response before moving on. Otherwise, the login results handler starts executing without an answer (because the server hasn't had time to respond).

Obviously, this is a horrible way to do things. What is the correct / swift idiomatic way to handle this?

func remote_login (email: String, password: String) -> Bool {
    login_result = false
    var jsonResults: NSDictionary = [:]
    let account = Account()
    let url = NSURL(string: "\(base_api_url())/login?email=\(email)&password=\(password)")
    let request = NSMutableURLRequest(URL: url!)

    request.HTTPMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")

    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        do {
            jsonResults = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
            account.setCredentials(jsonResults)  // we store an API token returned from a successful login; we don't store the users credentials locally
            self.login_result = true
        }
        catch {
            print("\n\n\n Login Error \n\n\n")
        }
    }) // task
    task.resume()
    sleep(3)  //need time for the response to come back...s/b far more elegant way to do this.
    return self.login_result
}
hawmack13
  • 233
  • 2
  • 17
  • You need to add a completion handler in which the response of the web api will be returned and then check in the code whether the response is correct or not – Bhagyalaxmi Poojary Feb 10 '16 at 12:36
  • Possible duplicate of [How can I get the Data from NSURLSession.sharedSession().dataTaskWithRequest](http://stackoverflow.com/questions/31264172/how-can-i-get-the-data-from-nsurlsession-sharedsession-datataskwithrequest) – Eric Aya Feb 10 '16 at 13:00

2 Answers2

2

Use a closure as your function argument, like so:

func remote_login(email: String, password: String, completion((Bool) -> Void)) {

Then, call the closure when finished (after JSON parsing) like this:

self.login_result = true
completion(login_result)

So, when you use the function, it will look like this:

remote_login("email", "password", {
    success in

    //Logged in successfully    
}

The closure will be called when the HTTP request finishes.

brimstone
  • 3,370
  • 3
  • 28
  • 49
  • I know this is an old answer, but it managed to resolve my issue. As a new Swift developer this was a lifesaver. Thanks @brimstone. – Terry Carter Mar 18 '20 at 19:52
0

As best I can see:

func remote_login (email: String, password: String, myFunctionCompletionHandler: ()->()) -> Bool {
    login_result = false
    var jsonResults: NSDictionary = [:]
    let account = Account()
    let url = NSURL(string: "\(base_api_url())/login?email=\(email)&password=\(password)")
    let request = NSMutableURLRequest(URL: url!)

    request.HTTPMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")

    let session = NSURLSession.sharedSession()

    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        do {
            jsonResults = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
            // we store an API token returned from a successful login; we don't store the users credentials locally
            account.setCredentials(jsonResults) 
            myFunctionCompletionHandler()
            // is this needed as it's handled by the handler?
            self.login_result = true
        }
        catch {
            myFunctionCompletionHandler()
            print("\n\n\n Login Error \n\n\n")
        }
    }) // task
    task.resume()
    return self.login_result
}

You might have to refactor things to handle the completionHandler a little better as I've just literally "plonked it in" but that should do the trick :)..

Just handle and pass the "failed and passed" correctly across to it :)

brimstone
  • 3,370
  • 3
  • 28
  • 49
Adrian Sluyters
  • 2,186
  • 1
  • 16
  • 21