Jon
Jon

Reputation: 29

How to call completionBlock in URLSession

I'm trying to learn how to use Rest API. I want to use Github API to login from iPhone first.

Here is my model (it's not completed yet):

mutating func gettingAceess(completionBlock: @escaping (_ data : Data?, _ response : URLResponse?, _ error : Error?) -> Void ) throws {
    guard username != "", password != "" else {
        throw loginError.emptyUsernameOrPassword
    }

    let loginString = "\(self.username):\(self.password)"
    let resourceUrl = "https://\(loginString)@api.github.com"
    guard let loggingUrl = URL(string: resourceUrl) else {fatalError()}

    let logging = URLSession.shared.dataTask(with: loggingUrl) { (data, response, error) in
        if error != nil {
            print("\(String(describing: error))")
        }
    }

    logging.resume()
}

In the controller, I want when user taps the login button, getting access function should be called.

@IBAction func pressLoginBtn(_ sender: UIButton) {
    if let username = usernameInput.text,  let password = passwordInput.text {
        var logginInformation = LogginReguest(username: username, password: password)
        do {
            try logginInformation.gettingAceess(completionBlock: ...)
        } catch {
  }

In the code front of the try, I don't know what I should write. Can anyone help me? Or I am doing the whole thing wrong?

Upvotes: 1

Views: 213

Answers (2)

Rahul
Rahul

Reputation: 2100

do {
  // if completion block is last argument; you can use trailing syntax
  try logginInformation.gettingAceess() { (data, response, error) in
     // as your completion block have this signature
     // completionBlock: @escaping (_ data : Data?, _ response : URLResponse?, _ error : Error?) -> Void)

  }
} catch {

You will need to update gettingAceess method also.

let logging = URLSession.shared.dataTask(with: loggingUrl) { (data, response, error) in
  if error != nil {
     print("\(String(describing: error))")
   }
   // This line passes this completion block to caller who can read it
   completion(data, response, error)
}

The above detail was how you will get completion block working in your example. The suggestion would be not to pass the URLSession completion block directly; rather process it.

enum NKError: Error {
  case networkError
}

func gettingAceess(completionBlock: @escaping (Result<[String: Any], NKError>) -> Void ) {
  let logging = URLSession.shared.dataTask(with: loggingUrl) { (data, response, error) in
    if error != nil {
      print("\(String(describing: error))")
      completion(.failure(NKError.networkError))
    }

    // Now I can check for status or data
    if let data = data {
      // parse the result and then return
      if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
        completion(.success(json))
      }
    }
  }
}

In this case, I am trying to pass the error or json to caller.

Upvotes: 0

vadian
vadian

Reputation: 285082

Your approach is wrong. First of all check for empty text fields already before calling the asynchronous task.

Secondly it's bad practice to make a function throws which contains an asynchronous task, in the completion block return the received data or the error.

This is an example with the Result struct returning the received Data as string or the error.

mutating func gettingAccess(completionBlock: @escaping (Result<String,Error>) -> Void)
{
    let loginString = "\(self.username):\(self.password)"
    let resourceUrl = "https://\(loginString)@api.github.com"
    guard let loggingUrl = URL(string: resourceUrl) else {fatalError()}

    let logging = URLSession.shared.dataTask(with: loggingUrl) { (data, _, error) in
        if let error = error {
            completion(.failure(error))
        } else {
            let string = String(data: data!, encoding: .utf8)!
            completion(.success(string))
        }
    }

    logging.resume()
}

@IBAction func pressLoginBtn(_ sender: UIButton) {
    guard usernameInput.hasText, passwordInput.hasText else { 
       // show an alert
       return
    }
    let logginInformation = LogginReguest(username: usernameInput.text!, password: passwordInput.text!)
    logginInformation.gettingAccess { result in
        switch result {
        case .success(let string): print(string)
        case .failure(let error): print(error)

        }
    } 
  }

Side note:

Please look for contemporary tutorials. The syntax (_ data : Data?, _ response : URLResponse?, _ error : Error?) with parameter labels and underscores is Swift 2 legacy syntax.

Upvotes: 1

Related Questions