it-roy-ru.com

Могу ли я установить файлы cookie, которые будут использоваться WKWebView?

Я пытаюсь переключить существующее приложение с UIWebView на WKWebView. Текущее приложение управляет входом/сеансом пользователей вне веб-представления и устанавливает файлы cookie, необходимые для аутентификации, в NSHTTPCookieStore. К сожалению, новый WKWebView не использует куки-файлы из NSHTTPCookieStorage. Есть ли другой способ добиться этого?

91
Col

Редактировать только для iOS 11+

Используйте WKHTTPCookieStore :

let cookie = HTTPCookie(properties: [
    .domain: "example.com",
    .path: "/",
    .name: "MyCookieName",
    .value: "MyCookieValue",
    .secure: "TRUE",
    .expires: NSDate(timeIntervalSinceNow: 31556926)
])! 

webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)

Поскольку вы извлекаете их из HTTPCookeStorage, вы можете сделать это:

let cookies = HTTPCookieStorage.shared.cookies ?? []
for (cookie) in cookies {
    webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
}

Старый ответ для iOS 10 и ниже

Если вам требуется, чтобы ваши куки были установлены в первоначальном запросе загрузки, вы можете установить их в NSMutableURLRequest. Поскольку файлы cookie - это просто специально отформатированный заголовок запроса, это может быть достигнуто следующим образом:

WKWebView * webView = /*set up your webView*/
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]];
[request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"];
// use stringWithFormat: in the above line to inject your values programmatically
[webView loadRequest:request];

Если вам требуется, чтобы на последующих запросах AJAX на странице были установлены файлы cookie, это можно сделать, просто используя WKUserScript для программной установки значений с помощью javascript при запуске документа следующим образом:

WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc] 
    initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
    injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// again, use stringWithFormat: in the above line to inject your values programmatically
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];

Объединение этих двух методов должно дать вам достаточно инструментов для передачи значений файлов cookie из Native App Land в Web View Land. Вы можете найти дополнительную информацию в api cookie javascript api на странице mozilla , если вам нужны более продвинутые файлы cookie. 

Да, это отстой, что Apple не поддерживает многие из тонкости UIWebView . Не уверен, будут ли они когда-либо поддерживать их, но, надеюсь, они скоро доберутся до этого. Надеюсь это поможет!

120
mattr

После игры с этот ответ (который был фантастически полезен :), нам пришлось внести несколько изменений:

  • Нам нужны веб-представления для работы с несколькими доменами без утечки частной информации cookie между этими доменами.
  • Нам нужно это, чтобы соблюдать безопасные куки
  • Если сервер изменяет значение cookie, мы хотим, чтобы наше приложение узнало об этом в NSHTTPCookieStorage
  • Если сервер изменяет значение cookie, мы не хотим, чтобы наши сценарии сбрасывали его обратно в исходное значение, когда вы переходите по ссылке/AJAX и т.д.

Таким образом, мы изменили наш код, чтобы быть этим;

Создание запроса

NSMutableURLRequest *request = [originalRequest mutableCopy];
NSString *validDomain = request.URL.Host;
const BOOL requestIsSecure = [request.URL.scheme isEqualToString:@"https"];

NSMutableArray *array = [NSMutableArray array];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    // Don't even bother with values containing a `'`
    if ([cookie.name rangeOfString:@"'"].location != NSNotFound) {
        NSLog(@"Skipping %@ because it contains a '", cookie.properties);
        continue;
    }

    // Is the cookie for current domain?
    if (![cookie.domain hasSuffix:validDomain]) {
        NSLog(@"Skipping %@ (because not %@)", cookie.properties, validDomain);
        continue;
    }

    // Are we secure only?
    if (cookie.secure && !requestIsSecure) {
        NSLog(@"Skipping %@ (because %@ not secure)", cookie.properties, request.URL.absoluteString);
        continue;
    }

    NSString *value = [NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value];
    [array addObject:value];
}

NSString *header = [array componentsJoinedByString:@";"];
[request setValue:header forHTTPHeaderField:@"Cookie"];

// Now perform the request...

Это гарантирует, что в первом запросе установлены правильные файлы cookie, без отправки файлов cookie из общего хранилища, предназначенных для других доменов, и без отправки каких-либо безопасных файлов cookie в небезопасный запрос.

Работа с дальнейшими запросами

Мы также должны убедиться, что для других запросов установлены файлы cookie. Это делается с помощью скрипта, который запускается при загрузке документа, который проверяет, есть ли набор файлов cookie, и если нет, присвойте ему значение в NSHTTPCookieStorage.

// Get the currently set cookie names in javascriptland
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];

for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    // Skip cookies that will break our script
    if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
        continue;
    }

    // Create a line that appends this cookie to the web view's document's cookies
    [script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptString];
}

WKUserContentController *userContentController = [[WKUserContentController alloc] init];
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                   forMainFrameOnly:NO];
[userContentController addUserScript:cookieInScript];

...

// Create a config out of that userContentController and specify it when we create our web view.
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;

self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config];

Работа с изменениями cookie

Нам также нужно иметь дело с сервером, меняющим значение куки. Это означает добавление другого скрипта для обратного вызова из веб-представления, которое мы создаем для обновления нашего NSHTTPCookieStorage.

WKUserScript *cookieOutScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.updateCookies.postMessage(document.cookie);"
                                                       injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                    forMainFrameOnly:NO];
[userContentController addUserScript:cookieOutScript];

[userContentController addScriptMessageHandler:webView
                                          name:@"updateCookies"];

и реализуя метод делегата для обновления любых файлов cookie, которые изменились, убедившись, что мы обновляем файлы cookie только из текущего домена!

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSArray<NSString *> *cookies = [message.body componentsSeparatedByString:@"; "];
    for (NSString *cookie in cookies) {
        // Get this cookie's name and value
        NSArray<NSString *> *comps = [cookie componentsSeparatedByString:@"="];
        if (comps.count < 2) {
            continue;
        }

        // Get the cookie in shared storage with that name
        NSHTTPCookie *localCookie = nil;
        for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:self.wk_webView.URL]) {
            if ([c.name isEqualToString:comps[0]]) {
                localCookie = c;
                break;
            }
        }

        // If there is a cookie with a stale value, update it now.
        if (localCookie) {
            NSMutableDictionary *props = [localCookie.properties mutableCopy];
            props[NSHTTPCookieValue] = comps[1];
            NSHTTPCookie *updatedCookie = [NSHTTPCookie cookieWithProperties:props];
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:updatedCookie];
        }
    }
}

Похоже, это решает наши проблемы с файлами cookie, и нам не приходится иметь дело с каждым местом, где мы используем WKWebView. Теперь мы можем просто использовать этот код в качестве помощника для создания наших веб-просмотров, и он прозрачно обновляет NSHTTPCookieStorage для нас.


Правка: Оказывается, я использовал частную категорию на NSHTTPCookie - вот код:

- (NSString *)wn_javascriptString {
    NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
                        self.name,
                        self.value,
                        self.domain,
                        self.path ?: @"/"];

    if (self.secure) {
        string = [string stringByAppendingString:@";secure=true"];
    }

    return string;
}
56
deanWombourne

работать на меня

func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
    let headerFields = navigationAction.request.allHTTPHeaderFields
    var headerIsPresent = contains(headerFields?.keys.array as! [String], "Cookie")

    if headerIsPresent {
        decisionHandler(WKNavigationActionPolicy.Allow)
    } else {
        let req = NSMutableURLRequest(URL: navigationAction.request.URL!)
        let cookies = yourCookieData
        let values = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
        req.allHTTPHeaderFields = values
        webView.loadRequest(req)

        decisionHandler(WKNavigationActionPolicy.Cancel)
    }
}
24
user3589213

Куки должны быть установлены в конфигурации до создания WKWebView. В противном случае, даже с обработчиком завершения WKHTTPCookieStoresetCookie файлы cookie не будут надежно синхронизированы с веб-представлением. Это восходит к этой строке из docs on WKWebViewConfiguration

@NSCopying var configuration: WKWebViewConfiguration { get }

Этот @NSCopying является чем-то вроде глубокой копии. Реализация выше моего понимания, но конечный результат заключается в том, что если вы не установите файлы cookie перед инициализацией веб-просмотра, вы не сможете рассчитывать на наличие файлов cookie. Это может усложнить архитектуру приложения, поскольку инициализация представления становится асинхронным процессом. Вы закончите с чем-то вроде этого

extension WKWebViewConfiguration {
    /// Async Factory method to acquire WKWebViewConfigurations packaged with system cookies
    static func cookiesIncluded(completion: @escaping (WKWebViewConfiguration?) -> Void) {
        let config = WKWebViewConfiguration()
        guard let cookies = HTTPCookieStorage.shared.cookies else {
            completion(config)
            return
        }
        // Use nonPersistent() or default() depending on if you want cookies persisted to disk
        // and shared between WKWebViews of the same app (default), or not persisted and not shared
        // across WKWebViews in the same app.
        let dataStore = WKWebsiteDataStore.nonPersistent()
        let waitGroup = DispatchGroup()
        for cookie in cookies {
            waitGroup.enter()
            dataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
        }
        waitGroup.notify(queue: DispatchQueue.main) {
            config.websiteDataStore = dataStore
            completion(config)
        }
    }
}

а затем использовать что-то вроде 

override func loadView() {
    view = UIView()
    WKWebViewConfiguration.cookiesIncluded { [weak self] config in
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.load(request)
        self.view = webView
    }
}

Приведенный выше пример откладывает создание представления до последнего возможного момента, другое решение будет заключаться в том, чтобы заранее создать конфигурацию или веб-представление и обработать асинхронную природу до создания контроллера представления.

Последнее замечание: как только вы создали это веб-представление, вы пустили его в дикую природу, вы не можете добавить больше файлов cookie, не используя методы, описанные в этот ответ . Тем не менее, вы можете использовать WKHTTPCookieStoreObserver api, чтобы хотя бы наблюдать изменения, происходящие с файлами cookie. Таким образом, если файл cookie сеанса обновляется в веб-просмотре, вы можете вручную обновить системный файл HTTPCookieStorage с помощью этого нового файла cookie, если это необходимо.

Подробнее об этом можно пропустить до 18:00 на этом 2017 WWDC Session Custom Web Content Загрузка . В начале этого сеанса есть пример вводящего в заблуждение кода, в котором пропускается тот факт, что веб-просмотр должен быть создан в обработчике завершения. 

cookieStore.setCookie(cookie!) {
    webView.load(loggedInURLRequest)
}

Живая демонстрация в 18:00 разъясняет это. 

Edit Начиная с Mojave Beta 7 и iOS 12 Beta 7, по крайней мере, я наблюдаю гораздо более согласованное поведение с файлами cookie. Даже метод setCookie(_:) позволяет устанавливать файлы cookie после создания WKWebView. Тем не менее, я считаю важным, чтобы не трогать переменная processPool. Функциональность настройки файлов cookie работает лучше всего, когда не создаются дополнительные пулы и когда это свойство не используется. Я думаю, можно с уверенностью сказать, что у нас были проблемы из-за некоторых ошибок в WebKit.

20
nteiss

Вот моя версия Mattrs решения в Swift для внедрения всех файлов cookie из HTTPCookieStorage. Это было сделано главным образом для добавления файла cookie аутентификации для создания пользовательского сеанса.

public func setupWebView() {
    let userContentController = WKUserContentController()
    if let cookies = HTTPCookieStorage.shared.cookies {
        let script = getJSCookiesString(for: cookies)
        let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
        userContentController.addUserScript(cookieScript)
    }
    let webViewConfig = WKWebViewConfiguration()
    webViewConfig.userContentController = userContentController

    self.webView = WKWebView(frame: self.webViewContainer.bounds, configuration: webViewConfig)
}

///Generates script to create given cookies
public func getJSCookiesString(for cookies: [HTTPCookie]) -> String {
    var result = ""
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
    dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"

    for cookie in cookies {
        result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
        if let date = cookie.expiresDate {
            result += "expires=\(dateFormatter.stringFromDate(date)); "
        }
        if (cookie.secure) {
            result += "secure; "
        }
        result += "'; "
    }
    return result
}
17
Misha

Обновление Swift 3:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let urlResponse = navigationResponse.response as? HTTPURLResponse,
       let url = urlResponse.url,
       let allHeaderFields = urlResponse.allHeaderFields as? [String : String] {
       let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url)
       HTTPCookieStorage.shared.setCookies(cookies , for: urlResponse.url!, mainDocumentURL: nil)
       decisionHandler(.allow)
    }
}
9
Deep Parekh

установить cookie 

self.webView.evaluateJavaScript("document.cookie='access_token=your token';domain='your domain';") { (data, error) -> Void in
        self.webView.reload()
}

удалить куки

self.webView.evaluateJavaScript("document.cookie='access_token=';domain='your domain';") { (data, error) -> Void in
        self.webView.reload()
}
7
cycDroid

В iOS 11 вы можете управлять cookie сейчас :), смотрите эту сессию: https://developer.Apple.com/videos/play/wwdc2017/220/

 enter image description here

7
Jacky

Посмотрев здесь различные ответы и не добившись успеха, я просмотрел документацию по WebKit и наткнулся на статический метод requestHeaderFields на HTTPCookie, который преобразует массив файлов cookie в формат, подходящий для поля заголовка. Комбинируя это с пониманием mattr об обновлении URLRequest перед загрузкой его с заголовками cookie, я получил финишную черту.

Swift 4.1:

var request = URLRequest(url: URL(string: "https://example.com/")!)
let headers = HTTPCookie.requestHeaderFields(with: cookies)
for (name, value) in headers {
    request.addValue(value, forHTTPHeaderField: name)
}

let webView = WKWebView(frame: self.view.frame)
webView.load(request)

Чтобы сделать это еще проще, используйте расширение:

extension WKWebView {
    func load(_ request: URLRequest, with cookies: [HTTPCookie]) {
        var request = request
        let headers = HTTPCookie.requestHeaderFields(with: cookies)
        for (name, value) in headers {
            request.addValue(value, forHTTPHeaderField: name)
        }

        load(request)
    }
}

Теперь это просто становится:

let request = URLRequest(url: URL(string: "https://example.com/")!)
let webView = WKWebView(frame: self.view.frame)
webView.load(request, with: cookies)

Это расширение также доступно в LionheartExtensions , если вам просто нужно решение для встраивания. Ура!

5
Dan Loewenherz

Пожалуйста, найдите решение, которое, скорее всего, будет работать для вас из коробки. В основном он изменен и обновлен для Swift 4 @ user3589213 s answer .

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    let headerKeys = navigationAction.request.allHTTPHeaderFields?.keys
    let hasCookies = headerKeys?.contains("Cookie") ?? false

    if hasCookies {
        decisionHandler(.allow)
    } else {
        let cookies = HTTPCookie.requestHeaderFields(with: HTTPCookieStorage.shared.cookies ?? [])

        var headers = navigationAction.request.allHTTPHeaderFields ?? [:]
        headers += cookies

        var req = navigationAction.request
        req.allHTTPHeaderFields = headers

        webView.load(req)

        decisionHandler(.cancel)
    }
}
2
Vadim Bulavin

Причиной публикации этого ответа является то, что я пробовал многие решения, но они не работают должным образом, большинство ответов не работают, при которых cookie не синхронизируются впервые. Пожалуйста, используйте это решение, оно работает как для iOS более 11.0, так и для iOS 11 до 8.0 также работать с синхронизацией cookie в первый раз. 

Для iOS> = 11.0 -- Swift 4.2 

Получите http cookies и установите в wkwebview cookie store таким образом, очень сложно загрузить ваш запрос в wkwebview , необходимо отправить запрос на загрузку, когда куки будут полностью установлены, вот функция, которую я написал.

Вызов функции с закрытием в завершение вы вызываете загрузку веб-просмотра. К вашему сведению, эта функция обрабатывает только iOS> = 11.0

self.WwebView.syncCookies {
    if let request = self.request {
       self.WwebView.load(request)
    }
}

Вот реализация для syncCookies function.

func syncCookies(completion:@escaping ()->Void) {

if #available(iOS 11.0, *) {

      if let yourCookie = "HERE_YOUR_HTTP_COOKIE_OBJECT" {
        self.configuration.websiteDataStore.httpCookieStore.setCookie(yourCookie, completionHandler: {
              completion()
        })
     }
  } else {
  //Falback just sent 
  completion()
}
}

Для iOS 8 до iOS 11

вам нужно настроить некоторые дополнительные вещи, которые вам нужны, чтобы установить два временных куки один с помощью WKUserScript и не забудьте также добавить куки в запрос, иначе ваш куки не синхронизируется в первый раз, и вы увидите, что ваша страница не загружается в первый раз должным образом. это черт, что я нашел, чтобы поддержать куки для iOS 8.0

перед вами Wkwebview создание объекта.

func setUpWebView() {

    let userController: WKUserContentController = WKUserContentController.init()

    if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
        if let cookies = HTTPCookieStorage.shared.cookies {
            if let script = getJSCookiesString(for: cookies) {
                cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
                userController.addUserScript(cookieScript!)
            }
        }
    }

    let webConfiguration = WKWebViewConfiguration()
    webConfiguration.processPool = BaseWebViewController.processPool


    webConfiguration.userContentController = userController


    let customFrame = CGRect.init(Origin: CGPoint.zero, size: CGSize.init(width: 0.0, height: self.webContainerView.frame.size.height))
    self.WwebView = WKWebView (frame: customFrame, configuration: webConfiguration)
    self.WwebView.translatesAutoresizingMaskIntoConstraints = false
    self.webContainerView.addSubview(self.WwebView)
    self.WwebView.uiDelegate = self
    self.WwebView.navigationDelegate = self
    self.WwebView.allowsBackForwardNavigationGestures = true // A Boolean value indicating whether horizontal swipe gestures will trigger back-forward list navigations
    self.WwebView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)


 self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .trailing, relatedBy: .equal, toItem: self.webContainerView, attribute: .trailing, multiplier: 1, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .leading, relatedBy: .equal, toItem: self.webContainerView, attribute: .leading, multiplier: 1, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .top, relatedBy: .equal, toItem: self.webContainerView, attribute: .top, multiplier: 1, constant: 0))
    self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .bottom, relatedBy: .equal, toItem: self.webContainerView, attribute: .bottom, multiplier: 1, constant: 0))


}

Сосредоточьтесь на этой функции getJSCookiesString

 public func getJSCookiesString(for cookies: [HTTPCookie]) -> String? {

    var result = ""
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
    dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"

    for cookie in cookies {
        if cookie.name == "yout_cookie_name_want_to_sync" {
            result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
            if let date = cookie.expiresDate {
                result += "expires=\(dateFormatter.string(from: date)); "
            }
            if (cookie.isSecure) {
                result += "secure; "
            }
            result += "'; "
        }

    }

    return result
}

Вот еще один шаг, когда wkuserscript не синхронизирует куки-файлы сразу, есть много способов загрузить первую страницу с куки-файлом. Во-первых, это перезагрузить веб-просмотр снова, если он завершает процесс, но я не рекомендую его использовать, это плохо для точки зрения пользователя. Черт возьми, всякий раз, когда вы готовы загрузить куки-файлы с установленными запросами в заголовок запроса, также не забудьте добавить проверку версии iOS. перед запросом загрузки вызовите эту функцию.

request?.addCookies()

я написал расширение для URLRequest  

extension URLRequest {

internal mutating func addCookies() {
    //"appCode=anAuY28ucmFrdXRlbi5yZXdhcmQuaW9zLXpOQlRTRmNiejNHSzR0S0xuMGFRb0NjbUg4Ql9JVWJH;rpga=kW69IPVSYZTo0JkZBicUnFxC1g5FtoHwdln59Z5RNXgJoMToSBW4xAMqtf0YDfto;rewardadid=D9F8CE68-CF18-4EE6-A076-CC951A4301F6;rewardheader=true"
    var cookiesStr: String = ""

    if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
        let mutableRequest = ((self as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
        if let yourCookie = "YOUR_HTTP_COOKIE_OBJECT" {
            // if have more than one cookies dont forget to add ";" at end
            cookiesStr += yourCookie.name + "=" + yourCookie.value + ";"

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

        }
    }

  }
}

теперь вы готовы пойти на тестирование iOS> 8  

2
Shauket Sheikh

Лучшее исправление для запросов XHR показано здесь

Версия Swift 4:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Swift.Void) {
    guard
        let response = navigationResponse.response as? HTTPURLResponse,
        let url = navigationResponse.response.url
    else {
        decisionHandler(.cancel)
        return
    }

    if let headerFields = response.allHeaderFields as? [String: String] {
        let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
        cookies.forEach { (cookie) in
            HTTPCookieStorage.shared.setCookie(cookie)
        }
    }

    decisionHandler(.allow)
}
0
Lloyd Keijzer

Это работает для меня: После setcookies, добавьте fetchdatarecords

   let cookiesSet = NetworkProvider.getCookies(forKey : 
    PaywallProvider.COOKIES_KEY, completionHandler: nil)
                let dispatchGroup = DispatchGroup()
                for (cookie) in cookiesSet {
                    if #available(iOS 11.0, *) {
                        dispatchGroup.enter()
                        self.webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie){
                            dispatchGroup.leave()
                            print ("cookie added: \(cookie.description)")
                            }
                        } else {
                                            // TODO Handle ios 10 Fallback on earlier versions
                        }
                    }
                    dispatchGroup.notify(queue: .main, execute: {


    self.webView.configuration.websiteDataStore.fetchDataRecords(ofTypes: 
    WKWebsiteDataStore.allWebsiteDataTypes()) { records in
                            records.forEach { record in

                                print("[WebCacheCleaner] Record \(record)")
                            }
                            self.webView.load(URLRequest(url: 
    self.dataController.premiumArticleURL , 
    cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData,
                                                         timeoutInterval: 10.0))
                        }

                    })
                }
0
adar tzeiri

При добавлении нескольких элементов cookie вы можете сделать это следующим образом: (path & domain требуется для каждого элемента)

NSString *cookie = [NSString stringWithFormat:@"document.cookie = 'p1=%@;path=/;domain=your.domain;';document.cookie = 'p2=%@;path=/;domain=your.domain;';document.cookie = 'p3=%@;path=/;domain=your.domain;';", p1_string, p2_string, p3_string];

WKUserScript *cookieScript = [[WKUserScript alloc]
            initWithSource:cookie
            injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];

[userContentController addUserScript:cookieScript];

в противном случае будет установлен только первый элемент cookie.

0
YanXing Ou

Если кто-то использует Alamofire, то это лучшее решение.

  let cookies = Alamofire.SessionManager.default.session.configuration.httpCookieStorage?.cookies(for: URL(string: BASE_URL)!)
  for (cookie) in cookies ?? [] {
      webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
  }
0
jeet.chanchawat

Я попробовал все ответы выше, но ни один из них не работает. После стольких попыток я наконец нашел надежный способ установить WKWebview cookie.

Сначала вы должны создать экземпляр WKProcessPool и установить его в WKWebViewConfiguration, который будет использоваться для инициализации самого WkWebview:

    private lazy var mainWebView: WKWebView = {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.processPool = WKProcessPool()
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = self
        return webView
    }()

Установка WKProcessPool является наиболее важным шагом здесь. WKWebview использует изоляцию процесса - это означает, что он работает в процессе, отличном от процесса вашего приложения. Иногда это может привести к конфликту и помешать правильной синхронизации файлов cookie с WKWebview. 

Теперь давайте посмотрим на определение WKProcessPool

Пул процессов, связанный с веб-представлением, определяется его конфигурацией веб-представления. Каждому веб-представлению присваивается собственный процесс веб-содержимого до тех пор, пока не будет достигнут предел процесса, определенный реализацией; после этого веб-представления с одним и тем же пулом процессов в конечном итоге обмениваются процессами веб-содержимого.

Обратите внимание на последнее предложение, если вы планируете использовать тот же WKWebview для запросов подпоследовательности.

веб-представления с тем же пулом процессов в конечном итоге обмениваются веб-контентом. процессы

я имею в виду, что если вы не используете один и тот же экземпляр WKProcessPool каждый раз, когда настраиваете WKWebView для одного и того же домена (возможно, у вас есть VC A, который содержит WKWebView, и вы хотите создать разные экземпляры VC A в разных местах), могут быть конфликты настроек куки. Чтобы решить эту проблему, после первого создания WKProcessPool для WKWebView, который загружает домен B, я сохраняю его в виде одиночного файла и использую тот же WKProcessPool каждый раз, когда мне нужно создать WKWebView, который загружает тот же домен B

private lazy var mainWebView: WKWebView = {
    let webConfiguration = WKWebViewConfiguration()
    if Enviroment.shared.processPool == nil {
        Enviroment.shared.processPool = WKProcessPool()
    }
    webConfiguration.processPool = Enviroment.shared.processPool!
    webConfiguration.processPool = WKProcessPool()
    let webView = WKWebView(frame: .zero, configuration: webConfiguration)
    webView.navigationDelegate = self
    return webView
}()

После процесса инициализации вы можете загрузить URLRequest внутри блока завершения из httpCookieStore.setCookie. Здесь вы должны прикрепить cookie к заголовку запроса, иначе он не будет работать. 

P/s: Я украл расширение из фантастического ответа Дана Лоуэнхерца, приведенного выше.

mainWebView.configuration.websiteDataStore.httpCookieStore.setCookie(your_cookie) {
        self.mainWebView.load(your_request, with: [your_cookie])
}

extension WKWebView {
   func load(_ request: URLRequest, with cookies: [HTTPCookie]) {
      var request = request
      let headers = HTTPCookie.requestHeaderFields(with: cookies)
      for (name, value) in headers {
         request.addValue(value, forHTTPHeaderField: name)
      }        
      load(request)
   }
}
0
Linh Ta