Swift:如何记住cookie以获取更多的http请求

Him*_*dav 17 cookies ios swift ios9

我正在开发一个登录应用程序.成功登录后,响应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()
Run Code Online (Sandbox Code Playgroud)

保存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())")
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

错误:

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

San*_*eep 25

如果您意识到cookie的使用,则服务器必须发送标头Set-Cookie以响应客户端请求.只需检查标题作为响应,您将看到Set-Cookie包含cookie的标题字段.

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

如果您使用默认或背景的URLSession,则无需URLSessionConfiguration进行任何更改以保存cookie.如果您查看默认文档URLSessionConfiguration,它会像这样描述,

默认会话配置使用基于磁盘的持久缓存(除非将结果下载到文件中),并将凭据存储在用户的密钥链中.它还将cookie(默认情况下)存储在与NSURLConnection和NSURLDownload类相同的共享cookie存储库中.

此外,您可以在此处URLSessionConfiguration的财产文档中进一步了解此httpCookieStorage 信息.

这是一小段代码,我将进一步用于测试cookie存储.

let sessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.httpCookieAcceptPolicy = .never
let customSession = URLSession(configuration: sessionConfiguration)

enum Result {
    case success(HTTPURLResponse, Data)
    case failure(Error)
}

func readCookie(forURL url: URL) -> [HTTPCookie] {
    let cookieStorage = HTTPCookieStorage.shared
    let cookies = cookieStorage.cookies(for: url) ?? []
    return cookies
}

func deleteCookies(forURL url: URL) {
    let cookieStorage = HTTPCookieStorage.shared

    for cookie in readCookie(forURL: url) {
        cookieStorage.deleteCookie(cookie)
    }
}

func storeCookies(_ cookies: [HTTPCookie], forURL url: URL) {
    let cookieStorage = HTTPCookieStorage.shared
    cookieStorage.setCookies(cookies,
                             for: url,
                             mainDocumentURL: nil)
}


func executeURLRequest(url: URL, inSession session: URLSession = .shared, completion: @escaping (Result) -> Void) {
    let task = session.dataTask(with: url) { data, response, error in

        if let response = response as? HTTPURLResponse,
            let data = data {
            completion(.success(response, data))
            return
        }

        if let error = error {
            completion(.failure(error))
            return
        }

        let error = NSError(domain: "com.cookiesetting.test", code: 101, userInfo: [NSLocalizedDescriptionKey: "Unknown error occurred"])
        completion(.failure(error))
    }
    task.resume()
}
Run Code Online (Sandbox Code Playgroud)

使用上面的代码段,我们首先测试默认会话保存cookie.

var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)

executeURLRequest(url: googleURL) { result in
    if case .success (let data) = result {
        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

        deleteCookies(forURL: googleURL)
        cookies = readCookie(forURL: googleURL)
        print("Cookies after deletion: ", cookies)
    }
}
Run Code Online (Sandbox Code Playgroud)

而且,这是我们得到的,

Cookies before request:  []
Cookies after request:  [<NSHTTPCookie
    version:0
    name:1P_JAR
    value:2018-09-26-15
    expiresDate:'2018-10-26 15:39:46 +0000'
    created:'2018-09-26 15:39:46 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
 path:"/" isSecure:FALSE>, <NSHTTPCookie
    version:0
    name:NID
    value:139=E3g4bKNRGcYoeFuaECpfsx_Efp64xONmVwcJS7f7PuZe8LayS5ZkGuz3f7z6eq7zoBm2z-opTvzX8YPzn8v1ebjH6iyt5-6yDYm9RE6XhXwHCZWs98_j7nb11u2EPnHI
    expiresDate:'2019-03-28 15:39:46 +0000'
    created:'2018-09-26 15:39:46 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/" isSecure:FALSE isHTTPOnly: YES>]
Cookies after deletion:  []
Run Code Online (Sandbox Code Playgroud)

URLSessionConfiguration还有一个属性httpCookieAcceptPolicy,引用如下:

此属性根据此配置确定会话中所有任务的cookie接受策略.

默认值为HTTPCookie.AcceptPolicy.onlyFromMainDocumentDomain.您可以将其更改为HTTPCookie.AcceptPolicy枚举类型中定义的任何常量.

如果您想要更直接地控制接受哪些cookie,请将此值设置为HTTPCookie.AcceptPolicy.never,然后使用allHeaderFields和cookies(withResponseHeaderFields:for :)方法自行从URL响应对象中提取cookie.

因此,如果您希望自己操作cookie,可以设置httpCookieAcceptPolicynever.

下面的代码显示,从不使用httpCookieAcceptPolicy时,不存储cookie

var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)

executeURLRequest(url: googleURL, inSession: customSession) { result in
    if case .success (let data) = result {
        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

    }
}
Run Code Online (Sandbox Code Playgroud)

记录以下内容;

Cookies before request:  []
Cookies after request:  []
Run Code Online (Sandbox Code Playgroud)

您可以看到使用.never for httpCookieStoragePolicy,系统不会将cookie存储到共享cookie存储.

你也可以自己存储cookie,看起来像这样,

自己存储cookie

deleteCookies(forURL: googleURL)
var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)
executeURLRequest(url: googleURL, inSession: customSession) { result in
    if  case let .success  (response, data) = result {
        guard let cookiesResponseHeader = response.allHeaderFields["Set-Cookie"] else {
            return
        }

        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

        let responseCookies = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String: String], for: googleURL)
        storeCookies(responseCookies, forURL: googleURL)
        cookies = readCookie(forURL: googleURL)
        print("Cookies after storing: ", cookies)

    }
}
Run Code Online (Sandbox Code Playgroud)

而且,这是上面的代码打印到控制台,

Cookies before request:  []
Cookies after request:  []
Cookies after storing:  [<NSHTTPCookie
    version:0
    name:1P_JAR
    value:2018-09-26-18
    expiresDate:'2018-10-26 18:35:23 +0000'
    created:'2018-09-26 18:35:23 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
 path:"/" isSecure:FALSE>, <NSHTTPCookie
    version:0
    name:NID
    value:139=D7GTUazWfeaB5Bcu1wN5I_Il2k6xALNiRZDX_DN9totQbnrP31gE0GzlsjCHDISUv8ulPq9G8Yu1p-GsZcVRw2fnrBROih-vtAVBic5UXFKUkG_ZbFQYKFprr4MPHDGS
    expiresDate:'2019-03-28 18:35:23 +0000'
    created:'2018-09-26 18:35:23 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/" isSecure:FALSE isHTTPOnly: YES>]
Run Code Online (Sandbox Code Playgroud)

上面的代码使用.neverHTTPCookieAcceptPolicy来进行URLSessionConfiguration,但是我们从响应中创建cookie并将它们自己存储到cookie存储中.