WKWebView setting Cookie not possible (iOS 11+)

2019-06-09 15:56发布

问题:

I am desperately trying to add a custom cookie to a WKWebView instance (without using Javascript or similar workarounds).

From iOS 11 and upwards, Apple provides an API to do this: The WKWebViews WKWebsiteDataStore has a property httpCookieStore.

Here is my (example) code:

import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        webView = WKWebView()
        view.addSubview(webView)
        super.viewDidLoad()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let cookie = HTTPCookie(properties: [
            HTTPCookiePropertyKey.domain : "google.com",
            HTTPCookiePropertyKey.path : "/",
            HTTPCookiePropertyKey.secure : true,
            HTTPCookiePropertyKey.name : "someCookieKey",
            HTTPCookiePropertyKey.value : "someCookieValue"])!

        let cookieStore = webView.configuration.websiteDataStore.httpCookieStore
        cookieStore.setCookie(cookie) {
            DispatchQueue.main.async {
                self.webView.load(URLRequest(url: URL(string: "https://google.com")!))
            }
        }
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        webView.frame = view.bounds
    }
}

After this, if I use webView.configuration.websiteDataStore.httpCookieStore.getAllCookies(completionHandler:) I see that my cookie is in the list of cookies.

However, when inspecting the webview using Safari's developer tools (using a iOS Simulator of course) the cookie does not show up.

I also tried to inspect the traffic using a HTTP proxy (Charles in my case) to see if the cookie in included in my HTTP requests. It is not.

What am I doing wrong here? How can I add a cookie to WKWebView (on iOS versions 11 and up)?

回答1:

A bit late but I want to share a solution that worked for me, I hope it helps someone facing the same issue on iOS 12 as well.

Here is the simplified workflow I used:

  1. Instantiate a WKWebsiteDataStore object
  2. Set the custom cookie to its httpCookieStore
  3. Wait for the cookie to be set
  4. Instantiate the WKWebView
  5. Load the request

To do that I have created an extension of WKWebViewConfiguration:

extension WKWebViewConfiguration {

static func includeCookie(cookie:HTTPCookie, preferences:WKPreferences, completion: @escaping (WKWebViewConfiguration?) -> Void) {
     let config = WKWebViewConfiguration()
     config.preferences = preferences

     let dataStore = WKWebsiteDataStore.nonPersistent()

     DispatchQueue.main.async {
        let waitGroup = DispatchGroup()

        waitGroup.enter()
        dataStore.httpCookieStore.setCookie(cookie) {
            waitGroup.leave()
        }

        waitGroup.notify(queue: DispatchQueue.main) {
            config.websiteDataStore = dataStore
            completion(config)
        }
    }
}

And for my example I have used it as follows:

override func viewDidLoad() {
  self.AddWebView()
}

private func addWebView(){

    let preferences = WKPreferences()
    preferences.javaScriptEnabled = true
    preferences.javaScriptCanOpenWindowsAutomatically = true

    let cookie = HTTPCookie(properties: [
        .domain: COOKIE_DOMAIN,
        .path: "/",
        .name: COOKIE_NAME,
        .value: myCookieValue,
        .secure: "TRUE",
        .expires: NSDate(timeIntervalSinceNow: 3600)
        ])

     //Makes sure the cookie is set before instantiating the webview and initiating the request
     if let myCookie = cookie {
        WKWebViewConfiguration.includeCookie(cookie: myCookie, preferences: preferences, completion: {
           [weak self] config in
              if let `self` = self {
                 if let configuration = config {
                    self.webView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width , height: self.view.frame.height), configuration: configuration)

                    self.view.addSubview(self.webView)
                    self.webView.load(self.customRequest)
                 }
              }
     }
}


回答2:

Any request to google.com redirects to www.google.com.

You would need to add www. to the domain field of the cookie. If the domain or the path doesn't match the request, the cookie won't be sent.

You can add the cookies explicitly.

let url = URL(string: "https://www.google.com")!
var request = URLRequest(url: url)
if let cookies = HTTPCookieStorage.shared.cookies(for: url) {
    request.allHTTPHeaderFields = HTTPCookie.requestHeaderFields(with: cookies)
}
self.webView.load(request)


回答3:

For iOS 11+ you really don't need to worry about cookies part it is very simple. Create your cookie like this. Don't make it secure true

let newcookie = HTTPCookie(properties: [
        .domain: "domain",
        .path: "/",
        .name: "name",
        .value: "vale",
        .secure: "FALSE",
        .expires: NSDate(timeIntervalSinceNow: 31556926)
        ])!

self.webview.configuration.websiteDataStore.httpCookieStore.setCookie(newcookie, completionHandler: {
                        // completion load your url request here. Better to add cookie in Request as well. like this way
       request.addCookies()
//enable cookie through request
        request.httpShouldHandleCookies = true

//load request in your webview.


                    })

Request extention add after cookie value ";"

extension URLRequest {

internal mutating func addCookies() {
    var cookiesStr: String = ""

        let mutableRequest = ((self as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
            cookiesStr += cookie.name + "=" + cookie.value + ";"                

            mutableRequest.setValue(cookiesStr, forHTTPHeaderField: "Cookie")
            self = mutableRequest as URLRequest

     }

}

Also i can see your are not setting WKWebViewConfiguration of wkwebview. Set configuration of your wkwebview. Also implement WKHTTPCookieStoreObserver and implement function.

    func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
    // Must implement otherwise wkwebview cookie not sync properly
    self.httpCookieStore.getAllCookies { (cookies) in
        cookies.forEach({ (cookie) in

        })
    }
}

Hope so this will work.