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データを取得するための回避策はありますか?
最後に、httpCookieStore
for WKWebsiteDataStore
がiOS 11に登場しました。
https://developer.Apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor
WKWebView
によって使用(作成)されたCookieは、実際にはNSHTTPCookieStorage.sharedHTTPCookieStorage()
に正しく保存されます。
問題は、WKWebView
がCookieをすぐに書き戻さないことです。独自のスケジュールでこれを行うと思います。たとえば、WKWebView
が閉じられている場合、または定期的に閉じられている場合。
したがって、最終的にはそこに行き着きますが、whenは予測不能です。
NSHTTPCookieStorage
を閉じることにより、共有WKWebView
に強制的に '同期'できる場合があります。これが機能するかどうかをお知らせください。
Update: Firefox for iOS では、WKWebView
が内部データをフラッシュするように強制することを思い出しましたWKProcessPool
を新しいものに置き換えることによるCookie。公式のAPIはありませんが、これが現時点で最も信頼できる回避策であると確信しています。
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>
コード
- 忘れずにここにソリューションコードを追加してください
- 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)) }
}
これは非常に古い質問であり、解決策はありますが、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の答えに従ってください。
Objective-CでWKHTTPCookieStoreを使用しました。これは永続的なCookieとセッションCookieの両方を取得するのに役立ちましたが、iOS 11以降でのみ機能します
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が削除されるようです
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];
}];
}
Stefanが述べたように、CookieはNSHTTPCookieStorage.sharedHTTPCookieStorage()
に保存されます
しかし、私の実験から、サーバーによって設定されたセッションCookieはNSHTTPCookieStorage.sharedHTTPCookieStorage()
には表示されないことがわかりました。
各WKWebView
がWKProcessPool
の同じインスタンスを共有している限り、これらのセッションCookieは各リクエストに対してサーバーに返されます。 WKWebView
のプロセスプールを変更すると、今後のすべてのリクエストのセッションCookieが実質的に削除されます。
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)
}
}
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)
}
iOS 11の場合、拡張子なし:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
//...
}
}
}
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に対して実行されません。
実際には、「decidePolicyForNavigationResponse」のメソッドで次の方法を使用してCookieを取得できますが、悲しいことに、それはセッションの完全/全体リストではありません。
let response = navigationResponse.response as! NSHTTPURLResponse
let headFields = response.allHeaderFields as! [String:String]
let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)