Swift: как запомнить файлы cookie для дальнейших HTTP-запросов

Я работаю над приложением для входа в систему. После успешного входа в систему возвращается данные cookie.
Как я могу использовать/сохранять эти данные для моих будущих запросов?
Для начала я пытаюсь сохранить его в NSHTTPCookieStorage. Но это тоже не работает.
Метод входа (частично):

let task = session.dataTaskWithRequest(request) { (data, responseData, error) -> Void in
            if let response = responseData as? NSHTTPURLResponse {
                statusCode = response.statusCode
                print("Response code: \(statusCode)")
            }

            var json: NSDictionary?
            do {
                json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
            } catch {
                print(error)
                err = error
            }

            if(statusCode != 200) {

                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Error could not parse JSON: '\(jsonStr)'")
            }
            else {

                print("Everything Looks good: \(responseData)")
                self.setCookies(responseData!)
                self.shouldPerformSegueWithIdentifier("showHomeController", sender: self)

            }
        }

        task?.resume()

Сохранить метод cookie

private func setCookies(response: NSURLResponse) {
        if let httpResponse = response as? NSHTTPURLResponse {
            let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(httpResponse.allHeaderFields, forURL: response.URL!) as! [NSHTTPCookie]
            NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookies(cookies, forURL: response.URL!, mainDocumentURL: nil)
            for cookie in cookies {
                var cookieProperties = [String: AnyObject]()
                cookieProperties[NSHTTPCookieName] = cookie.name
                cookieProperties[NSHTTPCookieValue] = cookie.value()
                cookieProperties[NSHTTPCookieDomain] = cookie.domain
                cookieProperties[NSHTTPCookiePath] = cookie.path
                cookieProperties[NSHTTPCookieVersion] = NSNumber(integer: cookie.version)
                cookieProperties[NSHTTPCookieExpires] = NSDate().dateByAddingTimeInterval(31536000)

                let newCookie = NSHTTPCookie(properties: cookieProperties)
                NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(newCookie!)

                println("name: \(cookie.name) value: \(cookie.value())")
            }
        }
    }

Ошибка:

Cannot invoke 'cookiesWithResponseHeaderFields' with an argument list of type '([NSObject : AnyObject], forURL: NSURL)'

Ответ 1

Если вы понимаете использование cookie, сервер должен отправить файл set-cookie заголовка в ответ на запрос клиента.

https://en.wikipedia.org/wiki/HTTP_cookie#Setting_a_cookie

Если вы используете NSURLSession и NSURLConfiguration с настройкой defaultConfiguration или backgroundSession, вам не нужно вносить какие-либо изменения в файл cookie. Система делает это сама по себе.

Из документации NSURLSessionConfiguration, документации HTTPCookieStorage,

Хранилище файлов cookie для хранения файлов cookie в этом сеансе. Эта свойство определяет объект хранения файлов cookie, используемый всеми задачами внутри сеансов на основе этой конфигурации. Чтобы отключить хранилище файлов cookie, установите это свойство равно нулю. Для стандартных и фоновых сеансов по умолчанию Значение - общий объект хранения файлов cookie. Для эфемерных сеансов Значение по умолчанию - это частный объект хранения файлов cookie, который хранит данные в только память и уничтожается, когда вы аннулируете сеанс.

Итак, я сделал небольшой эксперимент, чтобы прочитать cookie с google.com, и вот как это выглядит,

 private func setCookies(response: NSURLResponse) {
        let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookiesForURL(response.URL!)
        print(cookies)
    }

    let request = NSURLRequest(URL: NSURL(string: "https://google.com")!)

    configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

    session = NSURLSession(configuration: configuration)

    let task = session.dataTaskWithRequest(request) { data, response, error -> Void in
        if error == nil {
            self.setCookies(response!)
        }
    }
    task?.resume()

При этом он печатает следующее,

Optional([<NSHTTPCookie version:0 name:"NID" value:"69=j740tX4kU9JCWniPgXmngbD9ANWpzntszGg7Becz8FXCImLiVc3FmoHKdV-iOdd_PSCQeuVecwK6cyNfMOEW0gSz96CMEthN5uPZrR0J03sUKFuoRg4fO5A2ybwY6U_K" expiresDate:2016-01-15 22:01:36 +0000 created:2015-07-16 22:01:36 +0000 sessionOnly:FALSE domain:".google.fi" path:"/" isSecure:FALSE>, <NSHTTPCookie version:0 name:"PREF" value:"ID=1111111111111111:FF=0:TM=1437084096:LM=1437084096:V=1:S=TOU92HOWgdBrA6yl" expiresDate:2017-07-15 22:01:36 +0000 created:2015-07-16 22:01:36 +0000 sessionOnly:FALSE domain:".google.fi" path:"/" isSecure:FALSE>])

NSURLSessionConfiguration также имеет свойство HTTPCookieAcceptPolicy, которое цитирует следующее:

Константа политики, которая определяет, когда cookie должны быть приняты. Это свойство определяет политику принятия cookie для всех задач внутри сеансов на основе этой конфигурации. Значение по умолчанию NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain. Вы можете изменить его к любой из констант, определенных в NSHTTPCookieAcceptPolicy перечислимый тип. Если вам нужен более прямой контроль над тем, какие файлы cookie принято, установите это значение в NSHTTPCookieAcceptPolicyNever, а затем используйте allHeaderFields и cookiesWithResponseHeaderFields: forURL: методы для извлечения файлов cookie из объекта ответа URL.

Итак, в нем говорится, что если мы хотим иметь возможность манипулировать файлом cookie самим, мы должны установить политику в NSHTTPCookieAcceptPolicyNever и использовать метод allHeaderFields и cookiesWithResponseHeaderFields: forURL: для извлечения файлов cookie и сохранения.

Вот как вы это сделаете для этого примера.

let request = NSURLRequest(URL: NSURL(string: "https://google.com")!)

configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.Never

session = NSURLSession(configuration: configuration)

let task = session.dataTaskWithRequest(request) { data, response, error -> Void in
    if error == nil {
        self.setCookies(response!)
    }
}
task?.resume()

}

private func setCookies(response: NSURLResponse) {

    if let httpResponse = response as? NSHTTPURLResponse {
        if let headerFields = httpResponse.allHeaderFields as? [String: String] {
            let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headerFields, forURL: response.URL!)
            print(cookies)
        }
    }
}

Даже тогда вам не нужно устанавливать cookie на хранение самостоятельно. Обратите внимание на документацию HTTPCookieStorage, в которой говорится, что использование стандартных или фоновых сеансов всегда использует разделяемое хранилище, если вы не установили nil в свойство HTTPCookieStorage или не установили какое-либо другое хранилище.

Итак, если вы просто зарегистрируете файлы cookie по умолчанию, вы получите все файлы cookie сверху,

print(NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies)

Кажется, что можно определить собственное хранилище общих хранилищ общих хранилищ, которое можно использовать для расширений и приложений с iOS 9.

Итак, я не думаю, что действительно необходимо установить cookie самостоятельно, поскольку для этого есть встроенный механизм, если вам не нужен действительно точный контроль над свойствами cookie.

Ответ 2

класс func cookiesWithResponseHeaderFields (_ headerFields: [String: String], forURL URL: NSURL) → [NSHTTPCookie]

Обратите внимание, что headerFields является [String: String] Dictionary, и компилятор жалуется, что вы передаете [NSObject: AnyObject]

Ответ 3

для соединения URLSession вы можете использовать это (после того, как вы получили свой первый ответ):

var cookies =  URLSession.shared.configuration.httpCookieStorage?.cookies

и вы можете использовать эти файлы cookie следующим образом:

var session = URLSession.self
session.shared.configuration.httpCookieStorage?.setCookies(cookies, for: baseurl, mainDocumentURL: baseurl)
    let task = session.shared.dataTask(with: url)