Ответ 1
Наконец, httpCookieStore
для WKWebsiteDataStore
приземлился в iOS 11.
https://developer.apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor
в то время как получение файлов cookie из UIWebView
кажется простым, используя NSHTTPCookieStorage.sharedHTTPCookieStorage()
, кажется, что WKWebView
хранит файлы cookie в другом месте.
Я провел некоторое исследование, и я смог получить некоторые файлы cookie из захвата его из объекта NSHTTPURLResponse
. это, однако, не содержит всех файлов cookie, используемых WKWebView
:
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)
}
}
}
}
Как ни странно, существует также класс WKWebsiteDataStore
в ios 9, который отвечает за управление куки в 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
для WKWebsiteDataStore
приземлился в iOS 11.
https://developer.apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor
Cookies, используемые (создаваемые) WKWebView
, на самом деле правильно сохранены в NSHTTPCookieStorage.sharedHTTPCookieStorage()
.
Проблема заключается в том, что WKWebView
не сразу записывает файлы cookie. Я думаю, что это делается по собственному графику. Например, когда a WKWebView
закрыт или может быть периодически.
Итак, в конце концов, они все в порядке, но когда это непредсказуемо.
Возможно, вы сможете принудительно "синхронизировать" с общим NSHTTPCookieStorage
, закрыв WKWebView
. Пожалуйста, сообщите нам, если это сработает.
Обновить. Я только что вспомнил, что в Firefox для iOS мы вынуждаем WKWebView
очищать свои внутренние данные, включая файлы cookie, заменив его WKProcessPool
на новый. Официального 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 имеет встроенный контроллер представления
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)
}
Следуй за ответом Стефана Арента и Фенома.
Я использовал WKHTTPCookieStore в Objective-C. Это помогло мне получить как постоянные, так и сеансовые куки, но работает только в 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);
}
}];
Принуждение WKWebView для очистки внутренних данных путем замены его WKProcessPool, как описано в ответе Stefan, работало для меня в iOS 10 и 11, но только для постоянных файлов cookie; кажется, что файлы cookie сеанса удаляются, как описано в J. Thoo
Как упоминал Стефан, файлы cookie хранятся в
NSHTTPCookieStorage.sharedHTTPCookieStorage()
Однако из моих экспериментов я обнаружил, что cookie сеанса, установленные сервером, не отображается NSHTTPCookieStorage.sharedHTTPCookieStorage()
.
Пока каждый WKWebView
использует один и тот же экземпляр WKProcessPool
, эти куки сеанса будут переданы на сервер для каждого запроса. Если вы измените пул процессов для WKWebView
, вы фактически удалите файлы 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];
}];
}
Не тратьте свое время на извлечение файлов cookie с iOS 11 below device
, шансов на успех у вас очень мало. Из-за некоторых причин безопасности извлечение файлов 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)
}
}
На практике, я нашел в методе "solvePolicyForNavigationResponse", вы можете использовать следующий способ получения файлов cookie, но печально, что это не полный/цельный список для сеанса.
let response = navigationResponse.response as! NSHTTPURLResponse
let headFields = response.allHeaderFields as! [String:String]
let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)
В NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)
, что произойдет, если URL-адрес, где установлены файлы cookie, не является URL-адресом ответа на навигацию (URL-адрес, который вызывает навигацию)? Я замечаю, что URL-адрес обратного вызова, где установлены файлы cookie, никогда не вызывается в resolPolicyFor 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-адреса обратного вызова, так как сам обратный вызов не вызвал навигацию по страницам.
Моя проблема заключалась также в том, что getAllCookies() время от времени не стрелял. Это очень странно, но я бы сказал, что это происходит в 25% случаев. Это дает мне много проблем, потому что у меня есть токен в поле "Set-Cookie", перенаправленный запрос, который использовался для входа в социальные сети. Простой поиск файлов cookie здесь
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
NSHTTPCookieStorage.sharedHTTPCookieStorage()
не дает ничего, потому что поле маркера в этом случае по какой-то причине отсутствует.
Также не было токена, если бы я смотрел в словаре, как это
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void)
let response = navigationResponse.response as! NSHTTPURLResponse
let headFields = response.allHeaderFields as! [String:String]
let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)
В файлах cookie не обнаружено токена
Итак, я, наконец, закончил с обходным способом, принимающим токен от ответа напрямую и резкой строки, подобной этой
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let response = navigationResponse.response as? HTTPURLResponse, var token = response.allHeaderFields["Set-Cookie"] as? String {
let tokenConst = "token="
token = String(token.dropFirst(tokenConst.count))
print(token)
tokenOAuthReceived(token)
}
decisionHandler(.allow)
}
И если у вас много переадресаций в веб-форме, например, в моем случае, вам может потребоваться проверить токен для правильного URL-адреса, прежде чем использовать его
let urlString = response.url?.absoluteString, urlString.contains(find: RestApi.default.baseURLString)
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 {
//...
}
}
}
Этот пост содержит полезную информацию о обработке файлов cookie с помощью WKWebView. В соответствии с этим вы должны иметь возможность устанавливать и извлекать файлы cookie с помощью стандартных NSURLCache и NSHTTPCookie. Он также ссылается на использование WKProccessPool согласно замечанию Стефана.