user2629998
user2629998

Reputation:

Remembering cookies with Alamofire

I'm trying to make my Alamofire manager instance automatically remember & set cookies, here is my unsuccessful attempt :

let cfg = NSURLSessionConfiguration.defaultSessionConfiguration()
let cooks = NSHTTPCookieStorage.sharedHTTPCookieStorage()

// makes no difference whether it's set or left at default
cfg.HTTPCookieStorage = cooks
cfg.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.Always

let mgr = Alamofire.Manager(configuration: cfg)

mgr.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/cookies/set?stack=overflow"))).responseString {
    (_, _, response, _) in
    var resp = response // { "cookies": { "stack": "overflow" } }
    // becomes empty if cfg.HTTPCookieStorage set to nil
}

mgr.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/cookies"))).responseString {
    (_, _, response, _) in
    var resp = response // { "cookies": {} }
    // always empty no matter what
}

cooks.cookiesForURL(NSURL(string: "http://httpbin.org/cookies")) // 0 elements

The first URL sends a Set-Cookie : stack=overflow; Path=/ header and then redirects (302) to /cookies (equivalent of the second URL); this works fine in my browser (once I hit the first URL the second URL always display that cookie) so I'd like to replicate that behavior with Alamofire.

Upvotes: 10

Views: 12314

Answers (1)

Nate Cook
Nate Cook

Reputation: 93286

Your code is all working properly - you just aren't taking into account that you're calling asynchronous methods when you do a request. When that code is executed, this is what happens -- each call to mgr.request(...) initiates a new asynchronous call that doesn't resolve until after all the code above has executed:

| configure mgr
|
| request /cookies/set?... ----
|                              \
| request /cookies ----        |
|                      \       |
| cookiesForUrl()      |       |
|                      |       |
| [function returns]   |       |
                       |       |
...time passes...      |       |
                       /       |
  /cookies handler ----        |
                               |
...more time...                |
                               /
  /cookies/set? handler -------

(The order of those last two handlers is indeterminate - depends on the server / traffic / etc.)

So your request to "http://httpbin.org/cookies" doesn't actually include any cookies, since the request to set the cookies was just sent one instruction prior. To see the cookies, you'd need to wait until the first call returns to send the next request. I wrapped things in a UIViewController subclass:

class CookieViewController: UIViewController {
    // mgr needs to still exist when the response handlers are called
    var mgr: Alamofire.Manager!
    let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage()

    func configureManager() -> Alamofire.Manager {
        let cfg = NSURLSessionConfiguration.defaultSessionConfiguration()
        cfg.HTTPCookieStorage = cookies
        return Alamofire.Manager(configuration: cfg)
    }

    func setCookies() {
        mgr.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/cookies/set?stack=overflow")!)).responseString {
            (_, _, response, _) in
            var resp = response // { "cookies": { "stack": "overflow" } }
            println(resp)

            // the cookies are now a part of the URLSession - 
            // we can inspect them and call the next URL
            println(self.cookies.cookiesForURL(NSURL(string: "http://httpbin.org/cookies")!))
            self.checkCookies()
        }
    }

    func checkCookies() {
        mgr.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/cookies")!)).responseString {
            (_, _, response, _) in
            var resp = response // { "cookies": { "stack": "overflow" } }
            println(resp)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        mgr = configureManager()
        setCookies()
    }
}

Upvotes: 19

Related Questions