10

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.

  • 1
    Did you try providing cookies yourself using `HTTPAdditionalHeaders`and adding a Cookie header in `NSURLSessionConfiguration`? – Pintouch Oct 31 '14 at 09:58
  • @Pintouch I'm sure that would work but I'd like to use the already existing cookie storage features rather than keeping track of cookies and setting the header manually. –  Oct 31 '14 at 14:37
  • 1
    Yes I understand, but your code looks good for me. Try opening an issue on the AlamoFire GitHub – Pintouch Oct 31 '14 at 14:48
  • @Pintouch problem solved; look at the answer below. It was just a stupid mistake on my part, not realizing that the Alamofire methods are asynchronous. –  Nov 02 '14 at 14:25

1 Answers1

19

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()
    }
}
Nate Cook
  • 92,417
  • 32
  • 217
  • 178