qrolotrolpolo
qrolotrolpolo

Reputation: 1

How to set up completion handlers in API Call - Swift 5

I've run into a problem. I've made a service for API calls that returns a User object, and the API call works fine and it returns the User object.

However, I need to use completion and @escaping, and I'm not sure how to do it properly, since I'm new to Swift and iOS development.

The Code is below.

func login(with credentials: LoginCredentials) {
    let params = credentials
    AF.request(Service.baseURL.appending("/user/login"), method: .post, parameters: params, encoder: JSONParameterEncoder.default).responseJSON { response in

        switch response.result {
        case .success:
            print("Success")
        case.failure:
            print("Fail")
        }
    }

Also, how do I call a function when there is completion?

Upvotes: 0

Views: 1682

Answers (1)

valeCocoa
valeCocoa

Reputation: 344

It’s not clear what kind of argument (if it takes one) the completion for your method shall take, thus I’m gonna give you a couple of hints. Perhaps you want to also have the completion for your method execute on a queue specified as parameter in the call. Therefore you might structure your method’s signature in this way:

func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: @esacping (Bool) -> Void)

Here I’ve used the type Bool as argument for the completion to be executed, and defaulted the queue argument to be .main, assuming that you’re just interested in deliver as result argument in the completion a value of true in case you’ve gotten a .success response, false otherwise; I’m also assuming that this method will mainly be called with the completion being executed on the main thread for updating a state, hence the default value for the queue argument.

Therefore your method body could become like this:

func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: @escaping (Bool) -> Void) {
    let params = credentials
    AF.request(
        Service.baseURL.appending("/user/login"), 
        method: .post, 
        parameters: params, 
        encoder: JSONParameterEncoder.default
    ).responseJSON { response in
          switch response.result {
          case .success:
              queue.async { completion(true) }
          case.failure:
              queue.async { completion(false) }
          }
}

Now of course if you need to deliver something different (as in the token for the login session and an error in case of failure) then you’d better adopt a Result<T,Error> Swift enum as argument to pass to the completion. Without you giving more details in these regards, I can only give a generic suggestion for this matter, cause I cannot go into the specifics.

EDIT

As a bonus I'm also adding how you could structure this method with the new Swift concurrency model:

// Assuming you are returning either a value of type User or 
// throwing an error in case the login failed (as per your comments):
func login(with credentials: LoginCredentials) async throws -> User {
    withCheckedThrowingContinuation { continuation in 
        AF.request(
            Service.baseURL.appending("/user/login"), 
            method: .post, 
            parameters: credentials, 
            encoder: JSONParameterEncoder.default
        ).responseJSON { response in
              switch response.result {
              case .success:
                  let loggedUser = User(/* your logic to create the user to return*/)
              continuation.resume(returning: loggedUser)
              case.failure:
                  continuation.resume(throwing: User.Error.loginError)
              }
    }
}

// Here's possible way to implement the error on your User type
extension User {
    enum Error: Swift.Error {
        case loginError
        // add more cases for other errors related to this type
    }

}

Upvotes: 1

Related Questions