web-dev-qa-db-ja.com

WKWebViewからすべてのCookieを取得する

UIWebViewからCookieを取得することはNSHTTPCookieStorage.sharedHTTPCookieStorage()を使用することで簡単に思えますが、WKWebViewは別の場所にCookieを保存するようです。

私はいくつかの調査を行い、NSHTTPURLResponseオブジェクトからそれを取得することでいくつかのCookieを取得することができました。ただし、これにはWKWebViewが使用するすべてのCookieが含まれているわけではありません。

func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {

  if let httpResponse = navigationResponse.response as? NSHTTPURLResponse {
    if let headers = httpResponse.allHeaderFields as? [String: String], url = httpResponse.URL {
      let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)

      for cookie in cookies {
        logDebug(cookie.description)

        logDebug("found cookie " + cookie.name + " " + cookie.value)
      }
    }
  }
}

奇妙なことに、iOS 9にはWKWebsiteDataStoreのCookieを管理するクラスWKWebViewもありますが、クラスにはCookieデータを取得するためのパブリックメソッドが含まれていません。

let storage = WKWebsiteDataStore.defaultDataStore()

storage.fetchDataRecordsOfTypes([WKWebsiteDataTypeCookies], completionHandler: { (records) -> Void in
  for record in records {
    logDebug("cookie record is " + record.debugDescription)

    for dataType in record.dataTypes {
      logDebug("data type is " + dataType.debugDescription)

      // get cookie data??
    }
  }
})

Cookieデータを取得するための回避策はありますか?

54
aporat

最後に、httpCookieStore for WKWebsiteDataStoreがiOS 11に登場しました。

https://developer.Apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor

14
Tualatrix Chou

WKWebViewによって使用(作成)されたCookieは、実際にはNSHTTPCookieStorage.sharedHTTPCookieStorage()に正しく保存されます。

問題は、WKWebViewがCookieをすぐに書き戻さないことです。独自のスケジュールでこれを行うと思います。たとえば、WKWebViewが閉じられている場合、または定期的に閉じられている場合。

したがって、最終的にはそこに行き着きますが、whenは予測不能です。

NSHTTPCookieStorageを閉じることにより、共有WKWebViewに強制的に '同期'できる場合があります。これが機能するかどうかをお知らせください。

UpdateFirefox for iOS では、WKWebViewが内部データをフラッシュするように強制することを思い出しましたWKProcessPoolを新しいものに置き換えることによるCookie。公式のAPIはありませんが、これが現時点で最も信頼できる回避策であると確信しています。

64
Stefan Arentz

詳細

  • Xcode 9.2、Swift 4
  • Xcode 10.2(10E125)、Swift 5

解決

extension WKWebView {

    private var httpCookieStore: WKHTTPCookieStore  { return WKWebsiteDataStore.default().httpCookieStore }

    func getCookies(for domain: String? = nil, completion: @escaping ([String : Any])->())  {
        var cookieDict = [String : AnyObject]()
        httpCookieStore.getAllCookies { cookies in
            for cookie in cookies {
                if let domain = domain {
                    if cookie.domain.contains(domain) {
                        cookieDict[cookie.name] = cookie.properties as AnyObject?
                    }
                } else {
                    cookieDict[cookie.name] = cookie.properties as AnyObject?
                }
            }
            completion(cookieDict)
        }
    }
}

使用法

// get cookies for domain
webView.getCookies(for: url.Host) { data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)
}

// get all cookies
webView.getCookies() { data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)
}

完全なサンプル

Info.plist

info.plistトランスポートセキュリティ設定を追加します

 <key>NSAppTransportSecurity</key>
 <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
 </dict>

コード

  1. 忘れずにここにソリューションコードを追加してください
  2. ViewControllerにはView Controllerが組み込まれています
import UIKit
import WebKit

class ViewController: UIViewController {

    private lazy var url = URL(string: "https://google.com")!
    private weak var webView: WKWebView?

    func initWebView(configuration: WKWebViewConfiguration) {
        if webView != nil { return }
        let webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        view.addSubview(webView)
        self.webView = webView
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if webView == nil { initWebView(configuration: WKWebViewConfiguration()) }
        webView?.load(url: url)
    }
}

extension ViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if let url = webView.url {
            webView.getCookies(for: url.Host) { data in
                print("=========================================")
                print("\(url.absoluteString)")
                print(data)
            }
        }
    }
}

extension ViewController: WKUIDelegate {

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        // Push new screen to the navigation controller when need to open url in another "tab"
        if let url = navigationAction.request.url, navigationAction.targetFrame == nil {
            let viewController = ViewController()
            viewController.initWebView(configuration: configuration)
            viewController.url = url
            DispatchQueue.main.async { [weak self] in
                self?.navigationController?.pushViewController(viewController, animated: true)
            }
            return viewController.webView
        }
        return nil
    }
}

extension WKWebView {

    func load(urlString: String) {
        if let url = URL(string: urlString) { load(url: url) }
    }

    func load(url: URL) { load(URLRequest(url: url)) }
}

enter image description here

17

これは非常に古い質問であり、解決策はありますが、iOS 11以降でのみ動作します。 iOS 10以前(私のように)を扱っている人には、この方法を検討してもよいでしょう。それは私にとって完璧に機能します:

  • プロセスプールの強制リセット:
extension WKWebView {
    func refreshCookies() {
        self.configuration.processPool = WKProcessPool()
        // TO DO: Save your cookies,...
    }
}

->これは実際のデバイスでのみ動作します。

  • シミュレーターの場合、以下を追加する必要があります。
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let response = navigationResponse.response as? HTTPURLResponse,
       let allHttpHeaders = response.allHeaderFields as? [String: String],
       let responseUrl = response.url {
        let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHttpHeaders, for: responseUrl)

        for cookie in cookies {
            HTTPCookieStorage.shared.setCookie(cookie)
        }
    }

    decisionHandler(.allow)
}

Stefan ArentzとPhenomの答えに従ってください。

9
November Rain

Objective-CでWKHTTPCookieStoreを使用しました。これは永続的なCookieとセッションCookieの両方を取得するのに役立ちましたが、iOS 11以降でのみ機能します

https://developer.Apple.com/documentation/webkit/wkhttpcookiestore?changes=latest_minor&language=objc

 if (@available(iOS 11.0, *)) {
     WKHTTPCookieStore *cookieStore = _webView.configuration.websiteDataStore.httpCookieStore;
     [cookieStore getAllCookies:^(NSArray* cookies) {
        NSHTTPCookie *cookie;
        for(cookie in cookies){
            NSLog(@"cookie: %@", cookie);
        }
 }];

Stefanの答えで説明されているように、WKProcessPoolを置き換えることでWKWebViewに内部データをフラッシュさせることは、iOS 10および11で機能しましたが、永続的なCookieに対してのみ有効でした。 J. Thooが説明したように、セッションCookieが削除されるようです

2
Jorge Duque
if (@available(iOS 11.0, *)) {
  [webView.configuration.websiteDataStore.httpCookieStore
      getAllCookies:^(NSArray<NSHTTPCookie *> *_Nonnull cookies) {
        NSURLRequest *request =
            [[NSURLRequest alloc] initWithURL:self.URL]; //your URL
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session
            dataTaskWithRequest:request
              completionHandler:^(NSData *responseData, NSURLResponse *response,
                                  NSError *error) {
                //Do Something
              }];
        [task resume];
        [session.configuration.HTTPCookieStorage storeCookies:cookies forTask:task];
      }];
}
1
Vivek

Stefanが述べたように、CookieはNSHTTPCookieStorage.sharedHTTPCookieStorage()に保存されます

しかし、私の実験から、サーバーによって設定されたセッションCookieはNSHTTPCookieStorage.sharedHTTPCookieStorage()には表示されないことがわかりました。

WKWebViewWKProcessPoolの同じインスタンスを共有している限り、これらのセッションCookieは各リクエストに対してサーバーに返されます。 WKWebViewのプロセスプールを変更すると、今後のすべてのリクエストのセッションCookieが実質的に削除されます。

1
J.Thoo

iOS 11 below deviceからCookieを抽出するのに時間を無駄にしないでください。成功する可能性は非常に低くなります。セキュリティ上の理由により、Cookieの抽出がブロックされる場合があります。

これらのログを参照してください:

2019-02-07 00:05:45.548880+0530 MyApp[2278:280725] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C8.1:2][0x10fd776f0] get output frames failed, state 8196

2019-02-07 00:05:45.550915+0530 MyApp[2278:280725] TIC Read Status [8:0x0]: 1:57

以下のiOS 11デバイス用にビルドされたコードを試してください:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        let cookieValue = HTTPCookieStorage.shared.cookies(for: navigationResponse.response.url!)
        print(cookieValue!)
        let response = navigationResponse.response as! HTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

        let cookies = HTTPCookie.cookies(withResponseHeaderFields: headFields, for: response.url!)
        for cookie in cookies {
            print("name: \(cookie.name) value: \(cookie.value)")
        }
        decisionHandler(.allow)
    }

上記のコードは、Cookieの抽出がセキュリティ上の理由によりブロックされているため、空のCookie配列を提供します。

iOS 11以降を対象とする以下に従うことをお勧めします:

WKWebsiteDataStore.default().httpCookieStore.getAllCookies { (cookies) in
    for cookie in cookies {
        print(cookie)
    }
}
1
Shubham Mishra

Swift 5

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
        debugPrint(cookies.debugDescription)
    }

    decisionHandler(.allow)
}
0
Den

iOS 11の場合、拡張子なし:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
        for cookie in cookies {
            //...
        }
    }
}
0
Zeero0

NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)で、Cookieが設定されているURLがナビゲーション応答URL(ナビゲーションを引き起こすURL)ではない場合はどうなりますか? Cookieが設定されているコールバックURLは、decidePolicyFor navigationResponseでは呼び出されません。

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    let response = navigationResponse.response as! HTTPURLResponse
    let cookies  = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String : String], for: response.url!) 
}

コールバック自体がページナビゲーションを引き起こさないため、上記のデリゲートはコールバックURLに対して実行されません。

cookies(withResponseHeaderFields:for:)

0
Qin Zhengquan

実際には、「decidePolicyForNavigationResponse」のメソッドで次の方法を使用してCookieを取得できますが、悲しいことに、それはセッションの完全/全体リストではありません。

let response = navigationResponse.response as! NSHTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

        let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)
0
ikzjfr0