Reputation:
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
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