0

I think this is a pretty common use case, but I couldn't find any best way to achieve it.

Some parts of my ios application require login. How do I achieve the following pattern using Alamofire and swift.

.request (.GET 'login_required_endpoint')
      .responsejson(if statusCode == 401){
                login()
                continue with GETing
                      login_required_endpoint

What is the best way to achieve this.

  • Make a request
  • If server responds with 401(Unauthorized)
  • Ask user to login after saving all the request payload for previous request
  • After successful login, continue with request in [1] with payload saved

(I realize it is open-ended, but any help on how to make progress would be highly appreciated)

user462455
  • 12,838
  • 18
  • 65
  • 96

1 Answers1

2

With the below, you can call handleRequest instead of Alamofire's request

import Alamofire
import PromiseKit

enum ServerError: ErrorType {
    case Unauthorized
    // add others as necessary
}

func handleRequest(method: Alamofire.Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil) -> Promise<AnyObject> {
    return request(method, URLString, parameters: parameters).recover { error -> AnyObject in
        switch error {
        case ServerError.Unauthorized:
            return login().then {
                request(method, URLString, parameters: parameters)
            }
        default:
            throw error
        }
    }
}

private func request(method: Alamofire.Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil) -> Promise<AnyObject> {
    return Promise { fulfill, reject in
        Alamofire.request(method, URLString, parameters: parameters).responseJSON { response in
            switch response.result {
            case .Success(let json):
                fulfill(json)
            case .Failure(let error):
                if response.response?.statusCode == 401 {
                    reject(ServerError.Unauthorized)
                }
                else {
                    reject(error)
                }
            }
        }
    }
}

private func login() -> Promise<Void> {
    // do what you need here...
    return Promise()
}
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Thanks for the answer. How do I make sure the flow supports user entering wrong credentials more than once. It looks like your solution only supports one correct attempt . – user462455 Dec 15 '15 at 00:56
  • Hmm... If `handleRequest` returns an .Unauthorized error, then I would display an alert to the user. If the user then wants to try again, (s)he would simply need to repeat the operation that caused the login to come up in the first place. – Daniel T. Dec 15 '15 at 01:28
  • Otherwise, I suggest you post another question about how to retry a promise if it errors. – Daniel T. Dec 15 '15 at 01:35
  • Thanks! Question on your solution. ` return login().then { request(method, URLString, parameters: parameters) }` Client which calls handleRequest().then() is throwing an error which says the following `Could not cast value of type 'PromiseKit.Promise' to 'TaskSolver.Task' (0x1042562d0).` Not sure how to debug it. Any help would be appreciated – user462455 Dec 15 '15 at 02:12
  • Try `return login().then { () -> Promise in request(...` Let the system know what type it's dealing with. – Daniel T. Dec 15 '15 at 02:18
  • I still see same error. It seems to me that login promise is being returned prematurely. I found that, while debugging, I get the type cast exception before fulfill() method in login promise gets executed. – user462455 Dec 15 '15 at 02:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/97914/discussion-between-user462455-and-daniel-t). – user462455 Dec 15 '15 at 02:51
  • Changing the signature of close from ` .recover { error -> AnyObject } ` to ` .recover { error -> Promise } ` did the trick. – user462455 Dec 15 '15 at 04:02
  • I also have a generic error type `.Failure`. In switch statement where I check for error type it always falls down to the default case. Never shows `ServerError.Unauthorized`. Any ideas why? – Dmitry Dec 23 '15 at 01:25
  • @DanielT. Do you know is there a better and more concise way to know if error of type `ServerError.Unauthorized`? Here's a link to the relevant gist https://gist.github.com/dVaffection/0ea9975939eac9d3939d – Dmitry Dec 23 '15 at 21:16