ฉันสามารถตั้งค่าให้ WKWebView ใช้คุกกี้ได้หรือไม่


135

ฉันพยายามที่จะสลับแอปที่มีอยู่จากการUIWebView WKWebViewแอปปัจจุบันจัดการการเข้าสู่ระบบ / เซสชันของผู้ใช้ภายนอกwebviewและตั้งค่าที่cookiesจำเป็นสำหรับการตรวจสอบความถูกต้องลงในไฟล์NSHTTPCookieStore. น่าเสียดายที่ใหม่WKWebViewไม่ได้ใช้cookiesจากไฟล์NSHTTPCookieStorage. มีวิธีอื่นในการบรรลุเป้าหมายนี้หรือไม่?

คำตอบ:


187

แก้ไขสำหรับ 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 เนื่องจากคุกกี้เป็นเพียงส่วนหัวของคำขอที่มีรูปแบบพิเศษจึงสามารถทำได้ดังนี้:

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 ที่ตามมาบนเพจเพื่อตั้งค่าคุกกี้คุณสามารถทำได้โดยใช้ WKUserScript เพื่อตั้งค่าทางโปรแกรมผ่านทางจาวาสคริปต์เมื่อเริ่มต้นเอกสารดังนี้:

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

การรวมสองเทคนิคนี้จะทำให้คุณมีเครื่องมือเพียงพอในการโอนค่าคุกกี้จาก Native App Land ไปยัง Web View Land คุณสามารถค้นหาข้อมูลเพิ่มเติมได้ที่คุกกี้ javascript API ในหน้าของ Mozillaหากคุณต้องการคุกกี้ขั้นสูงเพิ่มเติม

ใช่มันแย่มากที่ Apple ไม่รองรับUIWebViewมากมาย ไม่แน่ใจว่าพวกเขาจะเคยสนับสนุนพวกเขาหรือไม่ แต่หวังว่าพวกเขาจะได้รับในเร็ว ๆ นี้ หวังว่านี่จะช่วยได้!


1
สถานที่ที่ดีที่สุดในการฉีดคุกกี้สำหรับคำขอในภายหลังคือที่ไหน? เช่นการโหลดหน้าเริ่มต้นจะกล่าวถึงในคำตอบด้านบน แต่จะเกิดอะไรขึ้นหากมีลิงก์บนหน้าเว็บที่นำไปสู่โดเมนเดียวกันและจำเป็นต้องใส่คุกกี้เดียวกันลงในคำขอด้วย didStartProvisionalNavigation?
Mason G. Zhwiti

1
ขออภัยมันไม่ได้ผลสำหรับคุณ ในใจของฉันตราบใดที่โดเมนเหมือนกันก็ไม่น่าจะมีปัญหาใด ๆ คุณสามารถตรวจสอบโค้ดอีกครั้งได้ไหมว่าลิงก์นั้นชี้ไปที่โดเมนเดียวกับที่คุณโหลดคำขอมา นอกจากนี้คุกกี้ยังสามารถ จำกัด เฉพาะ "เส้นทาง" ได้อีกด้วย บางทีนั่นอาจทำให้เกิดปัญหาบางอย่าง?
Mattr

11
โปรดทราบว่าเทคนิคจาวาสคริปต์ในการตั้งค่าคุกกี้จะใช้ไม่ได้กับคุกกี้ "HTTP เท่านั้น"
Ahmed Nasser

1
วิธีการข้างต้นใช้งานได้ดี ... แต่ฉันเห็นคุกกี้ซ้ำกันในการเรียก AJAX ครั้งต่อ ๆ ไป (ทำซ้ำเพียงครั้งเดียว)
Durga Vundavalli

1
@ Axel92Dev วิธีแก้ปัญหาคือเพื่อให้แน่ใจว่าคำขอแรกที่สร้างขึ้นจากการดูเว็บของคุณไปยังเซิร์ฟเวอร์ของคุณได้รับการตอบกลับที่แจ้งให้ Webview ตั้งค่าคุกกี้อีกครั้งด้วยการตั้งค่าสถานะ HTTPOnly (กล่าวคือตั้งค่าคุกกี้อีกครั้งในการตอบกลับ) คุณสามารถสร้าง API พิเศษเพื่อจุดประสงค์เดียวเมื่อเริ่มต้น webview จากนั้นใช้ webview ตามปกติเมื่อประสบความสำเร็จ
Ahmed Nasser

64

หลังจากเล่นกับคำตอบนี้ (ซึ่งเป็นประโยชน์อย่างมาก :) เราต้องทำการเปลี่ยนแปลงเล็กน้อย:

  • เราต้องการการดูเว็บเพื่อจัดการกับหลายโดเมนโดยไม่ให้ข้อมูลคุกกี้ส่วนตัวรั่วไหลระหว่างโดเมนเหล่านั้น
  • เราต้องการมันเพื่อให้เกียรติคุกกี้ที่ปลอดภัย
  • หากเซิร์ฟเวอร์เปลี่ยนค่าคุกกี้เราต้องการให้แอปของเราทราบ NSHTTPCookieStorage
  • หากเซิร์ฟเวอร์เปลี่ยนค่าคุกกี้เราไม่ต้องการให้สคริปต์ของเรารีเซ็ตกลับเป็นค่าเดิมเมื่อคุณไปที่ลิงก์ / 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...

สิ่งนี้ทำให้แน่ใจว่าคำขอแรกมีการตั้งค่าคุกกี้ที่ถูกต้องโดยไม่ต้องส่งคุกกี้ใด ๆ จากที่เก็บข้อมูลที่ใช้ร่วมกันซึ่งมีไว้สำหรับโดเมนอื่น ๆ และไม่มีการส่งคุกกี้ที่ปลอดภัยไปยังคำขอที่ไม่ปลอดภัย

การจัดการกับคำขอเพิ่มเติม

นอกจากนี้เราต้องตรวจสอบให้แน่ใจว่าคำขออื่น ๆ มีการตั้งค่าคุกกี้ 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];

การจัดการกับการเปลี่ยนแปลงคุกกี้

เราต้องจัดการกับเซิร์ฟเวอร์ที่เปลี่ยนค่าคุกกี้ 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"];

และใช้วิธีการมอบหมายเพื่ออัปเดตคุกกี้ที่มีการเปลี่ยนแปลงตรวจสอบให้แน่ใจว่าเรากำลังอัปเดตคุกกี้จากโดเมนปัจจุบันเท่านั้น!

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

ดูเหมือนว่าจะแก้ไขปัญหาคุกกี้ของเราได้โดยที่เราไม่ต้องจัดการกับแต่ละที่ที่เราใช้ 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;
}

6
ฉันได้รวมโค้ดของคุณไว้ในคลาสย่อยของ WKWebView แล้ว อย่าลังเลที่จะตรวจสอบgithub.com/haifengkao/YWebView
Hai Feng Kao

จะเกิดอะไรขึ้นถ้าคุกกี้ของคุณมีเครื่องหมาย = ในค่า? จะได้ผลไหม
iOSAddicted

@iOSAddicted ฉันคิดอย่างนั้น หากค่าของคุณคือa=bคุณจะลงเอยด้วยสตริงคุกกี้name=a=b;domain=.example.com;path=/- ฉันเชื่อว่าการแยกมาตรฐาน;และจากนั้นแยกค่าแรก =ในคู่คีย์ = ค่า ฉันจะทดสอบสิ่งนี้ :)
deanWombourne

คำตอบของคุณช่วยฉันได้มากอย่างไรก็ตามฉันต้องการเพิ่มบางสิ่งในโพสต์ของคุณมีความเสี่ยงหลายประการเมื่อใช้วิธีการอัปเดตของคุณเฟรมเวิร์ก JS บางตัวอาจสร้างคุกกี้ที่มีชื่อเดียวกัน แต่คนละโดเมนและหากคุณพยายามอัปเดต การใช้วิธี js ทำให้คุณมีความเสี่ยงสูงในการอัปเดตคุกกี้ด้วยค่าที่ไม่ถูกต้อง นอกจากนี้สำหรับเราแล้วสตริงคุกกี้ js ต้องถูกปลดจากแฟล็กที่ปลอดภัยเนื่องจากเซิร์ฟเวอร์ของเราทำการเปลี่ยนเส้นทางที่น่ารังเกียจระหว่าง http และ https ทำให้คุกกี้ที่ปลอดภัยไม่ปรากฏในบางหน้าในบางกรณีขอบที่น่ารังเกียจ
RicardoDuarte

จริงๆแล้วฉันคิดว่า บริษัท ที่ฉันอยู่ด้วยเมื่อฉันเขียนสิ่งนี้ต้องเพิ่มการป้องกันโดเมนบางอย่างหลังจากที่เผยแพร่จริง เราไม่เคย (afaik) พบกับปัญหาความปลอดภัย / ไม่ปลอดภัย - ฟังดูเหมือนฝันร้าย!
deanWombourne

42

ต้องตั้งค่าคุกกี้ในการกำหนดค่าก่อนที่WKWebViewจะสร้าง มิฉะนั้นแม้จะมีWKHTTPCookieStore's setCookieจัดการเสร็จคุกกี้จะไม่น่าเชื่อถือถูกซิงค์กับมุมมองเว็บ สิ่งนี้จะกลับไปที่บรรทัดนี้จากเอกสารบนWKWebViewConfiguration

@NSCopying var configuration: WKWebViewConfiguration { get }

นั่น@NSCopyingเป็นสำเนาที่ค่อนข้างลึก การใช้งานนั้นเหนือกว่าฉัน แต่ผลลัพธ์สุดท้ายก็คือถ้าคุณไม่ได้ตั้งค่าคุกกี้ก่อนเริ่มต้นการดูเว็บคุณจะไม่สามารถนับคุกกี้ที่อยู่ที่นั่นได้ ซึ่งอาจทำให้สถาปัตยกรรมของแอปซับซ้อนขึ้นเนื่องจากการเริ่มต้นมุมมองจะกลายเป็นกระบวนการแบบอะซิงโครนัส คุณจะจบลงด้วยสิ่งนี้

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

ตัวอย่างข้างต้นจะเลื่อนการสร้างมุมมองไปจนถึงช่วงเวลาสุดท้ายที่เป็นไปได้อีกวิธีหนึ่งคือการสร้าง config หรือ webview ล่วงหน้าและจัดการลักษณะอะซิงโครนัสก่อนที่จะสร้างตัวควบคุมมุมมอง

หมายเหตุสุดท้าย: เมื่อคุณสร้างมุมมองเว็บนี้คุณได้ตั้งค่าให้เป็นอิสระคุณจะไม่สามารถเพิ่มคุกกี้เพิ่มเติมได้หากไม่ใช้วิธีการที่อธิบายไว้ในคำตอบนี้ อย่างไรก็ตามคุณสามารถใช้WKHTTPCookieStoreObserverAPI เพื่อสังเกตการเปลี่ยนแปลงที่เกิดขึ้นกับคุกกี้ได้อย่างน้อยที่สุด ดังนั้นหากคุกกี้เซสชันได้รับการอัปเดตใน webview คุณสามารถอัปเดตระบบHTTPCookieStorageด้วยคุกกี้ใหม่นี้ได้ด้วยตนเองหากต้องการ

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ให้ข้ามไป 18:00 นี้เนื้อหากำลังโหลด 2017 WWDC เซสชันเว็บที่กำหนดเอง ในช่วงเริ่มต้นของเซสชันนี้มีตัวอย่างโค้ดหลอกลวงซึ่งมองข้ามความจริงที่ว่าควรสร้างมุมมองเว็บในเครื่องจัดการการดำเนินการเสร็จสิ้น

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

การสาธิตสดเวลา 18:00 น. อธิบายสิ่งนี้

แก้ไขตั้งแต่ Mojave Beta 7 และ iOS 12 Beta 7 เป็นอย่างน้อยฉันเห็นพฤติกรรมที่สอดคล้องกับคุกกี้มากขึ้น setCookie(_:)วิธีแม้จะปรากฏขึ้นเพื่อช่วยให้การตั้งค่าคุกกี้หลังจากที่WKWebViewได้ถูกสร้างขึ้น ฉันได้พบว่ามัน แต่สิ่งที่สำคัญที่จะได้สัมผัสprocessPoolตัวแปรที่ทั้งหมด ฟังก์ชันการตั้งค่าคุกกี้จะทำงานได้ดีที่สุดเมื่อไม่มีการสร้างพูลเพิ่มเติมและเมื่อคุณสมบัตินั้นถูกปล่อยให้อยู่ตามลำพัง ฉันคิดว่าปลอดภัยที่จะบอกว่าเราประสบปัญหาเนื่องจากข้อบกพร่องบางอย่างใน WebKit


ดูเหมือนว่าการจัดการ / การตั้งค่าคุกกี้จะน่าเชื่อถือกว่าใน Mojave 10.14 beta 3 และ iOS 12 beta 3
nteissler

6
คำตอบเชิงลึกและประเมินต่ำมาก
Nicolás Carrasco

1
ฉันยังคงมีปัญหานี้ใน iOS 12 พร้อมกับ WKWebView ที่โหลดไว้แล้ว บางครั้ง setCookie () จะซิงค์กับ WKWebView ทันทีบางครั้งก็ไม่สามารถจัดการได้บ้างประปราย
bmjohns

ฉันยังคงพบปัญหาเช่นกันเนื่องจากเรดาร์ได้รับการแก้ไข แต่มักจะน้อยกว่ามาก คุณเห็นคุกกี้ล้มเหลวบ่อยเพียงใด หากคุณมีโครงการที่ทำซ้ำได้และมีขนาดเล็กพอฉันขอแนะนำให้ส่งข้อผิดพลาดของ webkit ที่นี่: webkit.org/reporting-bugsนอกจากนี้คุณยังสามารถทวีตBrady Eidson (อย่างดี) สถาปนิก webkit ที่ apple ซึ่งตอบสนองต่อประเภทเหล่านี้ได้มาก รายงานและข้อบกพร่อง
nteissler

นี่คือคำตอบที่ถูกต้อง - ไม่จำเป็นต้องแปลคุกกี้เป็นฟิลด์ส่วนหัวในแต่ละคำขอ URL ด้วยตนเองเพียงแค่นั้นต้องใช้ setCookie () ตามที่อธิบายไว้ที่นี่
Guillaume Laurent

25

ทำงานให้ฉัน

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

การแฮ็กที่ยอดเยี่ยมโดยเฉพาะอย่างยิ่งการที่ iOS ที่ไม่เปลี่ยนสถานะเกี่ยวกับการลบล้างคุกกี้ใน WKWebView ที่มีอยู่ gotcha เพียงอย่างเดียวคือ WKNavigationKey ก่อนหน้านี้กลายเป็นค้าง รหัสอื่น ๆ อาจรออยู่โดยเปล่าประโยชน์ในรหัสเก่า
BaseZen

2
ถูกต้องหรือไม่ การชื่นชมมันอาจได้ผลในบางสถานการณ์ อย่างไรก็ตามความรับผิดชอบของวิธีการมอบสิทธิ์นี้คือการตัดสินใจนโยบายสำหรับนโยบาย ไม่โหลดคำขอจริงๆ ที่ได้ริเริ่มไว้ก่อนหน้านี้. ในกรณีนี้ไม่ทำให้การร้องขอถูกโหลดซ้ำสองครั้งหรือไม่?
Max MacLeod

2
@MaxMacLeod ในelseสภาพที่เขาเรียกการdecisionHandlerปิดด้วย.cancelดังนั้นจึงwebviewไม่โหลดคำขอเริ่มต้นจริงๆ หลังจากที่loadRequestถูกเรียกในelseเงื่อนไขวิธีการมอบหมายนี้จะถูกเรียกอีกครั้งสำหรับคำร้องขอนั้นและจะเข้าสู่ifเงื่อนไขเนื่องจากCookieจะมีส่วนหัวอยู่
halil_g

2
แม้ว่าจะไม่ได้ผลเมื่อคำขอเริ่มต้นมีการตั้งค่าคุกกี้ไว้แล้วเนื่องจากจะไม่เข้าelseเงื่อนไข
halil_g

โปรดทราบว่านี่คือ 1) ใช้ไม่ได้กับทุกสถานการณ์ตัวอย่างเช่นในกรณีที่การดูเว็บโหลดเฟรม 2) ไม่ปลอดภัย - อาจส่งคุกกี้ที่มีข้อมูลที่ละเอียดอ่อนไปยัง URL ของบุคคลที่สาม
Peter Prokop

20

นี่คือโซลูชันMattrsเวอร์ชันของฉันใน Swift สำหรับการฉีดคุกกี้ทั้งหมดจาก HTTPCookieStorage ส่วนใหญ่ทำเพื่อฉีดคุกกี้การตรวจสอบสิทธิ์เพื่อสร้างเซสชันผู้ใช้

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
}

ดีกว่าที่จะเพิ่มบรรทัดนี้เพื่อให้แน่ใจว่าการจัดรูปแบบสถานที่ถูกต้อง:dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
bubuxu

จะเรียกสิ่งนี้ได้ที่ไหน?
markhorrocks

สิ่งนี้ใช้ได้ดีสำหรับฉันใน Swift 4 (พร้อมการปรับเล็กน้อย)
Frédéric Adda

วิธีนี้ใช้งานได้ดีสำหรับฉัน แต่เพียงครั้งที่สองที่ฉันเยี่ยมชมไซต์ (ครั้งแรกที่ไม่ได้ตั้งค่าคุกกี้) มีใครเจอสิ่งนี้บ้าง?
MrChrisBarker

การโหลดครั้งแรกให้ข้อผิดพลาดในการโหลดครั้งที่สอง :( อาจเป็นปัญหาได้อย่างไร
Shauket Sheikh

10

ตั้งคุกกี้

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

9

อัปเดต 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)
    }
}

1
สวัสดีคุณสามารถเพิ่มรหัสเพื่อรับคุกกี้ได้HTTPCookieStorage.sharedหรือไม่?
markhorrocks

นี่เป็นวิธีเดียวที่ฉันได้รับ WKWebView เพื่อเพิ่มคุกกี้ให้กับทุกคำขอที่ทำโดย webview
Chicowitz

หากมีการตอบสนอง httponly คุกกี้คุณจะไม่สามารถรับค่าคุกกี้ด้วยวิธีนี้
brain

1
นี่เป็นเพียงการตั้งค่าคุกกี้กลับไปที่พื้นที่จัดเก็บ httpcookies รหัสที่ตั้งค่าคุกกี้สำหรับ wkwebview อยู่ที่ไหน
Shauket Sheikh

8

หลังจากดูคำตอบต่างๆที่นี่และไม่ประสบความสำเร็จใด ๆ ฉันได้อ่านเอกสารของ WebKit และพบกับrequestHeaderFieldsวิธีการแบบคงที่HTTPCookieซึ่งจะแปลงอาร์เรย์ของคุกกี้เป็นรูปแบบที่เหมาะสมกับฟิลด์ส่วนหัว การรวมสิ่งนี้เข้ากับข้อมูลเชิงลึกของ Mattr ในการอัปเดตURLRequestก่อนที่จะโหลดด้วยส่วนหัวของคุกกี้ทำให้ฉันผ่านเส้นชัย

สวิฟต์ 4.1, 4.2, 5.0:

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หากคุณต้องการเพียงแค่โซลูชันแบบดรอปอิน ไชโย!


1
@ShauketSheikh อืมสถานการณ์แบบนี้ใช้ไม่ได้เหรอ?
Dan Loewenherz

ฉันทดสอบโดยใช้โปรแกรมจำลอง ios 8 ดูเหมือนว่าจะไม่ส่งคุกกี้ ฉันได้ตรวจสอบอีกครั้ง
Shauket Sheikh

ฉันโพสต์คำตอบของฉันคุณสามารถลอง @Dan
Shauket Sheikh

7

ใน iOS 11 คุณสามารถจัดการคุกกี้ได้แล้ว :) ดูเซสชันนี้: https://developer.apple.com/videos/play/wwdc2017/220/

ป้อนคำอธิบายภาพที่นี่


2
@ShobhakarTiwari ทำไม? มีการเปลี่ยนแปลงใด ๆ เกิดขึ้นใน iOS11 อย่างเป็นทางการหรือไม่
Jacky

วิธีที่ดีที่สุดหากคุณรองรับเฉพาะ iOS 11 ขึ้นไปหากคุณต้องการรองรับเวอร์ชันก่อนหน้าให้ใช้ JavaScript ก่อนโหลดหน้า
PashaN

วิธีนี้ใช้ได้ผลสำหรับฉันยกเว้นในบางครั้งที่เมธอด setcookie ไม่เรียกใช้ตัวจัดการที่สมบูรณ์ซึ่งหมายความว่าบางครั้งหน้าเว็บของฉันไม่โหลด - เกิดขึ้นเฉพาะบนอุปกรณ์เท่านั้นเกิดขึ้นในครั้งที่ 3/4/5 เวลาปิดและเปิดใหม่ webview และหลังจากเกิดขึ้นครั้งเดียวมันจะยังคงเกิดขึ้นจนกว่าฉันจะรีเซ็ตแอพ - มีใครเจอปัญหานี้บ้างไหม
Binya Koatz

5

เหตุผลที่โพสต์คำตอบนี้คือฉันลองใช้วิธีแก้ปัญหามากมาย แต่ไม่มีใครทำงานได้อย่างถูกต้องคำตอบส่วนใหญ่ไม่ทำงานในกรณีที่ต้องตั้งค่าคุกกี้เป็นครั้งแรกและได้รับผลคุกกี้ที่ไม่ซิงค์ในครั้งแรกโปรดใช้วิธีนี้ใช้ได้กับทั้งสองอย่าง iOS> = 11.0 <= iOS 11 ถึง 8.0 ยังทำงานร่วมกับการซิงค์คุกกี้ครั้งแรก

สำหรับ iOS> = 11.0 - Swift 4.2

รับคุกกี้ httpและตั้งค่าในที่เก็บคุกกี้wkwebviewด้วยวิธีนี้เป็นจุดที่ยุ่งยากมากในการโหลดคำขอของคุณในwkwebviewต้องส่งคำขอเพื่อโหลดเมื่อคุกกี้จะถูกตั้งค่าอย่างสมบูรณ์นี่คือฟังก์ชั่นที่ฉันเขียน

ฟังก์ชั่นการโทรพร้อมการปิดเมื่อเสร็จสมบูรณ์คุณเรียกโหลด webview FYI ฟังก์ชั่นนี้จัดการเฉพาะ iOS> = 11.0

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

นี่คือการใช้งานสำหรับฟังก์ชันsyncCookies

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 ไม่ได้ซิงค์คุกกี้ในทันทีมีหลายอย่างที่ต้องโหลดหน้าครั้งแรกที่มีคุกกี้คือการโหลด webview อีกครั้งหากมันยุติกระบวนการ แต่ฉันไม่แนะนำให้ใช้มันไม่ดีสำหรับมุมมองของผู้ใช้ heck คือเมื่อใดก็ตามที่คุณพร้อมที่จะโหลดคุกกี้ชุดคำขอในส่วนหัวของคำขอเช่นกันวิธีนี้อย่าลืมเพิ่มการตรวจสอบเวอร์ชัน 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

โปรดค้นหาวิธีแก้ปัญหาที่น่าจะเหมาะกับคุณมากที่สุดนอกกรอบ โดยทั่วไปก็แก้ไขและปรับปรุงสำหรับสวิฟท์ 4 @ user3589213 's คำตอบ

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

1

ฉันได้ลองคำตอบทั้งหมดข้างต้นแล้ว แต่ไม่มีคำตอบใดได้ผล หลังจากพยายามหลายครั้งในที่สุดฉันก็พบวิธีที่เชื่อถือได้ในการตั้งค่าคุกกี้ WKWebview

ก่อนอื่นคุณต้องสร้างอินสแตนซ์ของ 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 ใช้ประโยชน์จากการแยกกระบวนการซึ่งหมายความว่ามันทำงานในกระบวนการที่แตกต่างจากกระบวนการของแอปของคุณ บางครั้งอาจทำให้เกิดความขัดแย้งและป้องกันไม่ให้คุกกี้ของคุณซิงค์อย่างถูกต้องกับ 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. ที่นี่คุณต้องแนบคุกกี้กับส่วนหัวของคำขอมิฉะนั้นจะไม่ทำงาน

P / s: ฉันขโมยส่วนขยายจากคำตอบที่ยอดเยี่ยมด้านบนโดย Dan Loewenherz

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

1

คำตอบของ nteiss เวอร์ชันของฉัน ผ่านการทดสอบiOS 11, 12, 13แล้ว ดูเหมือนว่าคุณไม่จำเป็นต้องใช้DispatchGroupในiOS 13อีกต่อไป

ฉันจะใช้ฟังก์ชั่นไม่คงที่includeCustomCookiesในWKWebViewConfigurationเพื่อที่ฉันสามารถอัปเดตทุกครั้งที่ผมสร้างใหม่cookiesWKWebViewConfiguration

extension WKWebViewConfiguration {
    func includeCustomCookies(cookies: [HTTPCookie], completion: @escaping  () -> Void) {
        let dataStore = WKWebsiteDataStore.nonPersistent()
        let waitGroup = DispatchGroup()

        for cookie in cookies {
            waitGroup.enter()
            dataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
        }

        waitGroup.notify(queue: DispatchQueue.main) {
            self.websiteDataStore = dataStore
            completion()
        }
    }
}

จากนั้นฉันจะใช้มันดังนี้:

let customUserAgent: String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15"

let customCookies: [HTTPCookie] = {
    let cookie1 = HTTPCookie(properties: [
        .domain: "yourdomain.com",
        .path: "/",
        .name: "auth_token",
        .value: APIManager.authToken
    ])!

    let cookie2 = HTTPCookie(properties: [
        .domain: "yourdomain.com",
        .path: "/",
        .name: "i18next",
        .value: "ru"
    ])!

    return [cookie1, cookie2]
}()

override func viewDidLoad() {
    super.viewDidLoad()

    activityIndicatorView.startAnimating()

    let webConfiguration = WKWebViewConfiguration()
    webConfiguration.includeCustomCookies(cookies: customCookies, completion: { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.webView = WKWebView(frame: strongSelf.view.bounds, configuration: webConfiguration)
        strongSelf.webView.customUserAgent = strongSelf.customUserAgent
        strongSelf.webView.navigationDelegate = strongSelf
        strongSelf.webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        strongSelf.view.addSubview(strongSelf.webView)
        strongSelf.view.bringSubviewToFront(strongSelf.activityIndicatorView)
        strongSelf.webView.load(strongSelf.request)
    })
}

0

การแก้ไขที่ดีกว่าสำหรับคำขอ 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

หากใครกำลังใช้ 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

สิ่งนี้ใช้ได้กับฉัน: หลังจาก 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

เมื่อเพิ่มรายการคุกกี้แบบทวีคูณคุณสามารถทำได้ดังนี้: ( 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];

มิฉะนั้นจะตั้งค่าเฉพาะรายการคุกกี้แรกเท่านั้น


0

คุณยังสามารถใช้ WKWebsiteDataStore เพื่อรับลักษณะการทำงานที่คล้ายกันกับ HTTPCookieStorage จาก UIWebView

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

0

โค้ดด้านล่างทำงานได้ดีในโครงการ Swift5 ของฉัน ลองโหลด url โดย WKWebView ด้านล่าง:

    private func loadURL(urlString: String) {
        let url = URL(string: urlString)
        guard let urlToLoad = url else { fatalError("Cannot find any URL") }

        // Cookies configuration
        var urlRequest = URLRequest(url: urlToLoad)
        if let cookies = HTTPCookieStorage.shared.cookies(for: urlToLoad) {
            let headers = HTTPCookie.requestHeaderFields(with: cookies)
            for header in headers { urlRequest.addValue(header.value, forHTTPHeaderField: header.key) }
        }

        webview.load(urlRequest)
    }

0

นี่คือทางออกของฉันในการจัดการกับคุกกี้และ WKWebView ใน iOS 9 หรือใหม่กว่า

import WebKit

extension WebView {

    enum LayoutMode {
        case fillContainer
    }

    func autoLayout(_ view: UIView?, mode: WebView.LayoutMode = .fillContainer) {
        guard let view = view else { return }
        self.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(self)

        switch mode {
        case .fillContainer:
                NSLayoutConstraint.activate([
                self.topAnchor.constraint(equalTo: view.topAnchor),
                self.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                self.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                self.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        }
    }

}

class WebView : WKWebView {

    var request : URLRequest?

    func load(url: URL, useSharedCookies: Bool = false) {
        if useSharedCookies, let cookies = HTTPCookieStorage.shared.cookies(for: url) {
            self.load(url: url, withCookies: cookies)
        } else {
            self.load(URLRequest(url: url))
        }
    }

    func load(url: URL, withCookies cookies: [HTTPCookie]) {
        self.request = URLRequest(url: url)
        let headers = HTTPCookie.requestHeaderFields(with: cookies)
        self.request?.allHTTPHeaderFields = headers
        self.load(request!)
    }

}

0

ข้อผิดพลาดนี้ที่ฉันทำคือฉันกำลังส่ง URL ทั้งหมดในแอตทริบิวต์โดเมนซึ่งควรเป็นชื่อโดเมนเท่านั้น

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

webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.