wake-up-neo.net

Kann ich die Cookies so einstellen, dass sie von einem WKWebView verwendet werden?

Ich versuche, eine vorhandene App von UIWebView auf WKWebView umzustellen. Die aktuelle App verwaltet die Anmeldung/Sitzung der Benutzer außerhalb der Webansicht und legt die für die Authentifizierung erforderlichen Cookies im NSHTTPCookieStore fest. Leider verwendet das neue WKWebView keine Cookies aus dem NSHTTPCookieStorage. Gibt es einen anderen Weg, dies zu erreichen?

91
Col

Nur für iOS 11+ bearbeiten

Verwenden Sie 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)

Da Sie sie von HTTPCookeStorage abrufen, können Sie Folgendes tun:

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

Alte Antwort für iOS 10 und darunter

Wenn Sie Ihre Cookies für die erste Ladeanforderung setzen müssen, können Sie sie in NSMutableURLRequest festlegen. Da es sich bei Cookies nur um einen speziell formatierten Anforderungsheader handelt, können Sie dies wie folgt erreichen:

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];

Wenn Sie nachfolgende AJAX -Anforderungen auf der Seite benötigen, um ihre Cookies setzen zu können, können Sie dies einfach tun, indem Sie WKUserScript verwenden, um die Werte programmgesteuert über Javascript beim Dokumentstart festzulegen.

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];

Durch die Kombination dieser beiden Techniken sollten Sie über genügend Tools verfügen, um Cookie-Werte von Native App Land in Web View Land zu übertragen. Weitere Informationen zum cookie Javascript-API finden Sie auf der Mozilla-Seite , falls Sie weitere fortgeschrittene Cookies benötigen. 

Ja, es ist ärgerlich, dass Apple nicht viele die Feinheiten von UIWebView unterstützt. Ich bin mir nicht sicher, ob sie sie jemals unterstützen werden, aber hoffentlich kommen sie bald dazu. Hoffe das hilft!

120
mattr

Nach dem Spielen mit dieser Antwort (was fantastisch hilfreich war :), mussten wir einige Änderungen vornehmen:

  • Wir benötigen die Web-Views, um mit mehreren Domains umzugehen, ohne private Cookie-Informationen zwischen diesen Domains zu verlieren
  • Wir brauchen es, um sichere Cookies zu ehren
  • Wenn der Server einen Cookie-Wert ändert, möchten wir, dass unsere App in NSHTTPCookieStorage darüber informiert wird.
  • Wenn der Server einen Cookie-Wert ändert, möchten wir nicht, dass unsere Skripts ihn auf seinen ursprünglichen Wert zurücksetzen, wenn Sie einem Link/AJAX usw. folgen.

Also haben wir unseren Code so geändert, dass er so ist.

Anfrage erstellen

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...

Dadurch wird sichergestellt, dass für die erste Anforderung die richtigen Cookies gesetzt sind, ohne dass Cookies aus dem gemeinsam genutzten Speicher für andere Domains gesendet werden und dass keine sicheren Cookies in eine unsichere Anfrage gesendet werden.

Bearbeitung weiterer Anfragen

Wir müssen auch sicherstellen, dass die Cookies für andere Anfragen gesetzt sind. Dies geschieht mit einem Skript, das beim Laden des Dokuments ausgeführt wird. Dabei wird geprüft, ob ein Cookie gesetzt ist. Wenn nicht, setzen Sie es auf den Wert in 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];

Umgang mit Cookie-Änderungen

Wir müssen uns auch damit befassen, dass der Server den Wert eines Cookies ändert. Dies bedeutet, dass Sie ein weiteres Skript hinzufügen, um aus der Web-Ansicht, die wir erstellen, um unsere NSHTTPCookieStorage zu aktualisieren, wieder aufzurufen.

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

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

und Implementierung der Delegat-Methode zum Aktualisieren von geänderten Cookies, um sicherzustellen, dass wir nur Cookies von der aktuellen Domäne aktualisieren!

- (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];
        }
    }
}

Dies scheint unsere Cookie-Probleme zu beheben, ohne dass wir mit jedem Ort, an dem wir WKWebView verwenden, anders umgehen müssen. Wir können diesen Code jetzt einfach als Hilfsmittel verwenden, um unsere Webviews zu erstellen, und NSHTTPCookieStorage wird transparent für uns aktualisiert.


BEARBEITEN: Es stellte sich heraus, dass ich bei NSHTTPCookie eine private Kategorie verwendet habe - hier der Code:

- (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

arbeite für mich

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

Die Cookies müssen in der Konfiguration gesetzt werden, bevor die WKWebView erstellt wird. Andernfalls werden die Cookies auch mit dem WKHTTPCookieStore-Beendigungs-Handler von setCookie nicht zuverlässig mit der Webansicht synchronisiert. Dies geht auf diese Zeile von der docs on WKWebViewConfiguration zurück.

@NSCopying var configuration: WKWebViewConfiguration { get }

Dieser @NSCopying ist so etwas wie eine tiefe Kopie. Die Implementierung ist nicht möglich, aber das Endergebnis ist, dass Sie, wenn Sie vor dem Initialisieren des Webview Cookies gesetzt haben, nicht damit rechnen können, dass die Cookies vorhanden sind. Dies kann die App-Architektur komplizieren, da das Initialisieren einer Ansicht zu einem asynchronen Prozess wird. Sie werden mit so etwas enden

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)
        }
    }
}

und dann etwas zu benutzen 

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

Das obige Beispiel verschiebt die Ansichtserzeugung bis zum letztmöglichen Zeitpunkt. Eine andere Lösung wäre, die Konfiguration oder die Webansicht frühzeitig zu erstellen und die asynchrone Natur vor der Erstellung eines View-Controllers zu behandeln.

Eine letzte Anmerkung: Nachdem Sie diesen Webview erstellt haben, haben Sie ihn losgelassen und Sie können keine weiteren Cookies hinzufügen, ohne die in diese Antwort beschriebenen Methoden zu verwenden. Sie können die WKHTTPCookieStoreObserver-API jedoch verwenden, um zumindest Änderungen an Cookies zu beobachten. Wenn also ein Session-Cookie in der Webansicht aktualisiert wird, können Sie die HTTPCookieStorage des Systems manuell mit diesem neuen Cookie aktualisieren, falls dies gewünscht wird.

Um mehr darüber zu erfahren, fahren Sie um 18:00 Uhr unter dieser WWDC-Sitzung 2017 mit benutzerdefinierten Webinhalten . Zu Beginn dieser Sitzung gibt es ein täuschendes Codebeispiel, in dem die Tatsache ausgelassen wird, dass die Webansicht im Completion-Handler erstellt werden sollte. 

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

Die Live-Demo um 18:00 Uhr verdeutlicht dies. 

Edit Ab Mojave Beta 7 und iOS 12 Beta 7 ist das Verhalten bei Cookies viel konsistenter. Die setCookie(_:)-Methode scheint sogar das Setzen von Cookies zuzulassen, nachdem die WKWebView erstellt wurde. Ich fand es jedoch wichtig, nicht zu berühren die Variable processPool überhaupt. Die Cookie-Einstellungsfunktion funktioniert am besten, wenn keine zusätzlichen Pools erstellt werden und diese Eigenschaft in Ruhe gelassen wird. Ich denke, man kann mit Sicherheit sagen, dass wir Probleme mit einigen Fehlern in WebKit hatten.

20
nteiss

Hier ist meine Version von Mattrs Solution in Swift zum Einfügen aller Cookies von HTTPCookieStorage. Dies wurde hauptsächlich zum Einfügen eines Authentifizierungscookies zum Erstellen einer Benutzersitzung durchgeführt.

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 Update:

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 setzen 

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

cookie löschen

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

In iOS 11 können Sie jetzt ein Cookie verwalten :), siehe diese Sitzung: https://developer.Apple.com/videos/play/wwdc2017/220/

 enter image description here

7
Jacky

Nachdem ich hier verschiedene Antworten durchgesehen hatte und keinen Erfolg hatte, durchkämmte ich die WebKit-Dokumentation und stolperte über die requestHeaderFields static-Methode für HTTPCookie, die ein Array von Cookies in ein für ein Headerfeld geeignetes Format konvertiert. Durch die Kombination mit mattrs Einsicht der Aktualisierung der URLRequest vor dem Laden mit den Cookie-Headern gelangte ich durch die Ziellinie.

Schnell 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)

Um dies noch einfacher zu machen, verwenden Sie eine Erweiterung:

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)
    }
}

Jetzt wird es einfach zu:

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

Diese Erweiterung ist auch in LionheartExtensions verfügbar, wenn Sie nur eine Drop-In-Lösung wünschen. Prost!

5
Dan Loewenherz

Finden Sie die Lösung, die am ehesten für Sie funktionieren wird. Grundsätzlich ist es für Swift 4 @ user3589213 's answer modifiziert und aktualisiert worden.

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

Der Grund für diese Antwort ist, dass ich viele Lösungen ausprobiert habe, aber nicht richtig funktioniert. Die meisten Antworten funktionieren nicht. Welches Cookie wird beim ersten Mal nicht synchronisiert? Bitte verwenden Sie diese Lösung, um sowohl für iOS größer als 11.0 und weniger als iOS 11 bis 8.0 zu funktionieren erstmalig mit der Cookie-Synchronisierung arbeiten. 

Für iOS> = 11.0 -- Swift 4.2 

Holen Sie sich http cookies und setzen Sie diese in wkwebview cookie store. Auf diese Weise ist es sehr knifflig, Ihre Anfrage in wkwebview zu laden. Sie müssen eine Anfrage zum Laden senden, wenn Cookies vollständig gesetzt werden Funktion, die ich geschrieben habe.

Funktion mit Schließung aufrufen Zum Abschluss rufen Sie load webview auf. Zu Ihrer Information handelt diese Funktion nur mit iOS> = 11.0

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

Hier ist die Implementierung für syncCookies Funktion.

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()
}
}

Für iOS 8 bis iOS 11

sie müssen einige zusätzliche Einstellungen vornehmen, die Sie benötigen, um zwei Zeitcookies durch Verwendung von WKUserScript zu setzen. Vergessen Sie auch nicht, Cookies in Anfrage hinzuzufügen. Andernfalls wird Ihr Cookie beim ersten Mal nicht synchronisiert richtig. Dies ist der Haken, den ich gefunden habe, um Cookies für iOS 8.0 zu unterstützen

bevor Sie das Wkwebview-Objekt erstellen.

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))


}

Fokus auf diese Funktion 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
}

Hier ist ein anderer Schritt wkuserscript, Cookies nicht sofort zu synchronisieren. Es ist sehr schwierig, die erste Seite mit Cookie zu laden. WebView erneut zu laden, wenn der Prozess beendet wird. Ich empfehle jedoch nicht, es zu verwenden. Es ist nicht für die Benutzerperspektive geeignet , heck ist immer dann, wenn Sie bereit sind, Request-Set-Cookies in den Request-Header zu laden. Vergessen Sie nicht, die iOS-Versionsprüfung hinzuzufügen. Vor dem Ladeauftrag rufen Sie diese Funktion auf.

request?.addCookies()

ich schrieb die Erweiterung für 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

        }
    }

  }
}

jetzt können Sie loslegen, um iOS zu testen> 8  

2
Shauket Sheikh

Die bessere Lösung für XHR-Anfragen wird angezeigt hier

Swift 4 Version:

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

Dies funktioniert für mich: Nach Setcookies fetchdatarecords hinzufügen

   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

Wenn Sie mehrere Cookie-Elemente hinzufügen, können Sie dies folgendermaßen tun: (path & domain ist für jedes Element erforderlich)

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];

andernfalls wird nur das erste Cookie-Objekt gesetzt.

0
YanXing Ou

Sie können auch WKWebsiteDataStore verwenden, um ein ähnliches Verhalten wie HTTPCookieStorage von UIWebView zu erhalten.

let dataStore = WKWebsiteDataStore.default()
let cookies = HTTPCookieStorage.shared.cookies ?? [HTTPCookie]()
cookies.forEach({
    dataStore.httpCookieStore.setCookie($0, completionHandler: nil)
})
0
Tigu

Wenn jemand Alamofire verwendet, ist dies die bessere Lösung.

  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

Ich habe alle obigen Antworten ausprobiert, aber keine davon funktioniert. Nach so vielen Versuchen habe ich endlich einen zuverlässigen Weg gefunden, WKWebview-Cookie zu setzen.

Zuerst müssen Sie eine Instanz von WKProcessPool erstellen und diese auf die WKWebViewConfiguration setzen, die zum Initialisieren des WkWebview selbst verwendet werden soll:

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

Das Einstellen von WKProcessPool ist hier der wichtigste Schritt. WKWebview verwendet die Prozessisolation, was bedeutet, dass es in einem anderen Prozess als dem Prozess Ihrer App ausgeführt wird. Dies kann manchmal zu Konflikten führen und verhindern, dass Ihr Cookie ordnungsgemäß mit dem WKWebview synchronisiert wird. 

Schauen wir uns nun die Definition von WKProcessPool an.

Der einer Webansicht zugeordnete Prozesspool wird durch seine Webansichtskonfiguration angegeben. Jede Webansicht erhält einen eigenen Webinhaltsprozess, bis eine durch die Implementierung definierte Prozessgrenze erreicht wird. Danach teilen Web Views mit demselben Prozesspool Web Content-Prozesse.

Achten Sie auf den letzten Satz, wenn Sie denselben WKWebview für Untersequenzenanfragen verwenden möchten

webansichten mit demselben Prozesspool teilen schließlich den Webinhalt Prozesse

ich meine damit, wenn Sie nicht jedes Mal dieselbe Instanz von WKProcessPool verwenden, wenn Sie eine WKWebView für dieselbe Domäne konfigurieren (möglicherweise haben Sie ein VC A, das eine WKWebView enthält, und Sie möchten verschiedene Instanzen von VC A (an verschiedenen Stellen), kann es zu Konflikt-Cookies kommen. Um das Problem zu lösen, speichere ich es nach der ersten Erstellung des WKProcessPool für eine WKWebView, die Domäne B lädt, in einem Singleton und verwende denselben WKProcessPool jedes Mal, wenn ich eine WKWebView erstellen muss, die dieselbe Domäne B lädt

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
}()

Nach dem Initialisierungsprozess können Sie eine URLRequest in den Beendigungsblock von httpCookieStore.setCookie laden. Hier muss Sie müssen den Cookie an den Anforderungsheader anfügen, sonst funktioniert er nicht. 

P/s: Ich habe die Erweiterung aus der fantastischen Antwort von Dan Loewenherz gestohlen

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