Çerezleri bir WKWebView tarafından kullanılacak şekilde ayarlayabilir miyim?


135

Mevcut bir uygulamayı ile arasında UIWebViewdeğiştirmeye çalışıyorum WKWebView. Mevcut uygulama, kullanıcı oturumunu / oturumunu dışında yönetir ve kimlik doğrulaması webviewiçin cookiesgerekli olanı NSHTTPCookieStore. Maalesef yeni WKWebViewkullanmaz cookiesdan NSHTTPCookieStorage. Bunu başarmanın başka bir yolu var mı?

Yanıtlar:


187

Yalnızca iOS 11+ için düzenleyin

WKHTTPCookieStore'u kullanın :

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

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

Bunları HTTPCookeStorage'dan çektiğiniz için bunu yapabilirsiniz:

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

İOS 10 ve altı için eski cevap

Çerezlerinizin ilk yükleme talebinde ayarlanmasını istiyorsanız, onları NSMutableURLRequest'te ayarlayabilirsiniz. Çerezler yalnızca özel olarak biçimlendirilmiş bir istek başlığı olduğundan, bu şu şekilde gerçekleştirilebilir:

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

Sayfadaki sonraki AJAX isteklerinin çerezlerinin ayarlanmasını istiyorsanız, bu, yalnızca WKUserScript kullanarak değerleri programlı olarak javascript aracılığıyla belge başlangıcında şu şekilde ayarlayarak gerçekleştirilebilir:

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

Bu iki tekniği birleştirmek, çerez değerlerini Yerel Uygulama Alanından Web Görünümü Alanına aktarmanız için size yeterli araç sağlayacaktır. Biraz daha gelişmiş çerezlere ihtiyaç duyarsanız , çerez javascript API'sı hakkında Mozilla'nın sayfasında daha fazla bilgi bulabilirsiniz .

Evet, Apple'ın UIWebView'in pek çok özelliğini desteklememesi berbat . Onları destekleyeceklerinden emin değilim, ama umarım yakında buna devam edecekler. Bu yardımcı olur umarım!


1
Sonraki istekler için tanımlama bilgilerini enjekte etmek için en iyi yer neresidir? Örneğin, ilk sayfa yüklemesi yukarıdaki cevapta ele alınmıştır, ancak sayfada aynı etki alanına da götüren bağlantılar varsa ve aynı zamanda isteğe aynı çerezlerin enjekte edilmesi gerekiyorsa ne olur? didStartProvisionalNavigation?
Mason G. Zhwiti

1
üzgünüm senin için çalışmıyor. Bence alan adları aynı olduğu sürece herhangi bir sorun olmamalı. Bağlantının, isteği yüklediğiniz etki alanına işaret ettiğini iki kez kontrol edebilir misiniz? Ayrıca tanımlama bilgileri, belirli bir "yol" ile sınırlandırılabilir. Belki bu bazı sorunlara neden oluyor?
mattr

11
Çerezleri ayarlamaya yönelik javascript tekniğinin "Yalnızca HTTP" çerezleri için çalışmayacağını unutmayın.
Ahmed Nasser

1
Yukarıdaki yöntem harika çalışıyor ... ancak sonraki AJAX çağrılarında yinelenen çerezleri görebiliyordum (yalnızca bir kez çoğaltıldı).
Durga Vundavalli

1
@ Axel92Dev bir geçici çözüm, web görünümünüzden sunucunuza yapılan ilk isteğin, web görünümüne tanımlama bilgilerini HTTPOnly işaretiyle yeniden ayarlamasını açıkça söyleyen bir yanıt almasını sağlamak olabilir (örn: yanıtta tanımlama bilgilerini yeniden ayarlayın). Web görünümünü başlatırken yalnızca bu amaç için özel bir API oluşturabilir, ardından başarı için web görünümünü normal şekilde kullanabilirsiniz.
Ahmed Nasser

64

Bu cevapla oynadıktan sonra (ki bu fevkalade yardımcı oldu :) birkaç değişiklik yapmak zorunda kaldık:

  • Bu alanlar arasında gizli çerez bilgilerini sızdırmadan birden çok alanla başa çıkmak için web görünümlerine ihtiyacımız var.
  • Güvenli çerezleri onurlandırmak için buna ihtiyacımız var
  • Sunucu bir çerez değerini değiştirirse, uygulamamızın bunu bilmesini isteriz. NSHTTPCookieStorage
  • Sunucu bir çerez değerini değiştirirse, bir bağlantıyı / AJAX'ı vb. Takip ettiğinizde komut dosyalarımızın onu orijinal değerine sıfırlamasını istemeyiz.

Biz de kodumuzu şu şekilde değiştirdik;

Bir istek oluşturma

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

Bu, diğer etki alanları için paylaşılan depolamadan herhangi bir tanımlama bilgisi göndermeden ve güvenli olmayan bir isteğe güvenli tanımlama bilgisi göndermeden, ilk isteğin doğru tanımlama bilgilerine sahip olmasını sağlar.

Diğer taleplerle ilgilenmek

Ayrıca diğer isteklerde çerezlerin ayarlandığından emin olmamız gerekir. Bu, bir çerez seti olup olmadığını kontrol eden ve eğer yoksa, içindeki değere ayarlayan belge yükünde çalışan bir komut dosyası kullanılarak yapılır 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];

Çerez değişiklikleriyle başa çıkmak

Ayrıca bir çerezin değerini değiştiren sunucuyla da ilgilenmemiz gerekiyor. Bu, bizim oluşturduğumuz web görünümünden geri aramak için başka bir komut dosyası eklemek anlamına gelir 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"];

ve değişen çerezleri güncellemek için temsilci yöntemini uygulayarak, yalnızca mevcut etki alanından çerezleri güncellediğimizden emin olun!

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

Bu, WKWebView'ı kullandığımız her yerle farklı şekilde ilgilenmemize gerek kalmadan çerez sorunlarımızı çözüyor gibi görünüyor. Artık bu kodu web görünümlerimizi oluşturmak için yardımcı olarak kullanabiliriz ve bizim için şeffaf bir şekilde güncellenir NSHTTPCookieStorage.


DÜZENLEME: NSHTTPCookie'de özel bir kategori kullandığım ortaya çıktı - işte kod:

- (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
Kodunuzu bir WKWebView alt sınıfına sardım. Kontrol etmekten
Hai Feng Kao

Çerezleriniz değerde = işaretleri içeriyorsa ne olur? Bu işe yarar mı?
iOSAddicted

@iOSAddicted Ben öyle düşünüyorum . Değeriniz a=bolsaydı, çerez dizesini elde edecektiniz name=a=b;domain=.example.com;path=/- inanıyorum ki standart bölünüyor ;ve ardından anahtar = değer çiftinde ilk bölünüyor =. Bunu test
ederdim

Yanıtınız bana çok yardımcı oldu, ancak gönderinize bir şeyler eklemek isterim, güncelleme yönteminizi kullanırken birkaç risk vardır, bazı JS çerçeveleri aynı ada ancak farklı etki alanına sahip çerezler oluşturabilir ve siz onu güncellemeye çalışırsanız js yöntemlerini kullandığınızda, yanlış bir değere sahip bir çerezi güncelleme riskiniz yüksektir. Ayrıca bizim için js çerez dizesinin güvenli bayrağından çıkarılması gerekiyordu, çünkü sunucumuz http ve https arasında kötü yönlendirmeler yapıyor ve bazı kötü uç durumlarda bazı sayfalarda güvenli çerezlerin bulunmamasına neden oluyordu.
RicardoDuarte

Aslında bunu yazdığımda birlikte olduğum şirketin, yayınlandıktan sonra ona bir miktar alan koruması eklemek zorunda kaldığını düşünüyorum. Asla (afaik) güvenli / güvensiz problemle karşılaşmadık - bir kabusa benziyor!
deanWombourne

42

Çerezler WKWebView, oluşturulmadan önce konfigürasyonda ayarlanmalıdır . Aksi takdirde, hatta birlikte WKHTTPCookieStorebireyin setCookietamamlama işleyicisi, kurabiye güvenilir web görünümüne senkronize edilmez. Bu bu çizgiye geri gider dokümanlar üzerindeWKWebViewConfiguration

@NSCopying var configuration: WKWebViewConfiguration { get }

Bu @NSCopyingbiraz derin bir kopya. Uygulama benim dışımda, ancak sonuç, web görünümünü başlatmadan önce çerezleri ayarlamadıkça, orada bulunan çerezlere güvenemezsiniz. Bu, uygulama mimarisini karmaşıklaştırabilir çünkü bir görünümü başlatmak eşzamansız bir süreç haline gelir. Bunun gibi bir şeyle son bulacaksın

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

ve sonra bunu kullanmak için

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

Yukarıdaki örnek, olası son ana kadar görünüm oluşturmayı erteler; başka bir çözüm, yapılandırmayı veya web görünümünü çok önceden oluşturmak ve bir görünüm denetleyicisinin yaratılmasından önce asenkron doğayı ele almak olabilir.

Son bir not: Bu web görünümünü oluşturduktan sonra, onu serbest bıraktığınızda, bu cevapta açıklanan yöntemleri kullanmadan daha fazla çerez ekleyemezsiniz . Bununla birlikte, WKHTTPCookieStoreObserverapi'yi en azından çerezlerde meydana gelen değişiklikleri gözlemlemek için kullanabilirsiniz . Bu nedenle, web görünümünde bir oturum çerezi güncellenirse, HTTPCookieStorageistenirse bu yeni çerezle sistemin manuel olarak güncellenmesini sağlayabilirsiniz .

Bununla ilgili daha fazla bilgi için, bu 2017 WWDC Oturumu Özel Web İçeriği Yüklemesinde 18: 00'a atlayın . Bu oturumun başında, web görünümünün tamamlama işleyicisinde oluşturulması gerektiği gerçeğini göz ardı eden aldatıcı bir kod örneği vardır.

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

18: 00'daki canlı demo bunu açıklığa kavuşturuyor.

En azından Mojave Beta 7 ve iOS 12 Beta 7'den itibaren düzenle , çerezlerle çok daha tutarlı davranışlar görüyorum. setCookie(_:)Yöntem sonra bile ayar çerezlere izin görünüyor WKWebViewoluşturuldu. Ben, bu önemli olmasa da buldunuz dokunmazprocessPool hiç değişkeni. Tanımlama bilgisi ayarı işlevi en iyi, ek havuz oluşturulmadığında ve bu özellik yalnız bırakıldığında çalışır. WebKit'teki bazı hatalar nedeniyle sorun yaşadığımızı söylemenin güvenli olduğunu düşünüyorum.


Görünüşe göre çerez işleme / ayarı Mojave 10.14 beta 3 ve iOS 12 beta 3'te daha güvenilir
nteissler

6
Çok derinlemesine ve küçümsenen cevap
Nicolás Carrasco

1
Zaten yüklü bir WKWebView ile iOS 12'de hala bu sorunu yaşıyorum. Bazen setCookie () aslında hemen WKWebView ile senkronize edilir, bazen kullanımı biraz düzensiz hale
getirmez

Radarın düzeltildiği iddia edildiğinden beri sorunları hala görüyorum, ancak çok daha az sıklıkta. Çerez hatasını ne sıklıkla görüyorsunuz? Yeniden üretilebilir, yeterince küçük bir projeniz varsa, buraya bir webkit hatası göndermenizi gerçekten tavsiye ederim: webkit.org/reporting-bugs Ayrıca, Apple'da bu türlere çok duyarlı bir webkit mimarı Brady Eidson'a (güzelce) tweet atabilirsiniz . raporlar ve hatalar.
nteissler

bu doğru cevaptır - çerezleri her bir URLRequest'te başlık alanları olarak manuel olarak çevirmeye gerek yoktur, sadece setCookie () burada açıklandığı gibi kullanılmalıdır.
Guillaume Laurent

25

benim için çalış

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

Müthiş hack, özellikle iOS'un mevcut bir WKWebView'daki bir çerezi geçersiz kılma konusunda ne kadar etkili olduğu. Tek sorun, önceki WKNavigationKey'in eskimiş olmasıdır. Diğer kod, eskisinin üzerinde boşuna bekliyor olabilir.
BaseZen

2
bu doğru mu? Bazı durumlarda işe yarayabileceğini takdir ediyorum. Bununla birlikte, bu temsilci yönteminin sorumluluğu - rulePolicyForNavigationAction - politikaya karar vermektir; aslında isteği yüklememek. Bu daha önce başlatıldı. Hangi durumda bu, talebin iki kez yüklenmesine neden olmaz mı?
Max MacLeod

2
In @MaxMacLeod elseo aradığını koşulu decisionHandlerile kapatılması .cancelböylece webviewaslında ilk isteği yüklemez. Koşulda loadRequestçağrıldıktan sonra elsebu istek için bu delege yöntemi tekrar çağrılacak ifve Cookiebaşlık mevcut olacağı için duruma geçecektir.
halil_g

2
Yine de, ilk istekte zaten bazı çerezler ayarlandığında bu işe yaramaz, çünkü hiçbir zaman duruma girmeyecektir else.
halil_g

Bunun 1) Her durumda işe yaramadığını unutmayın - örneğin, web görünümü çerçeveleri yüklediğinde 2) Güvenli değil - hassas bilgiler içeren çerezleri üçüncü taraf URL'sine gönderebilir
Peter Prokop

20

İşte HTTPCookieStorage'dan tüm çerezleri enjekte etmek için Swift'deki Mattrs çözümümün sürümü . Bu, esas olarak bir kullanıcı oturumu oluşturmak için bir kimlik doğrulama tanımlama bilgisi enjekte etmek için yapıldı.

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
}

yerel ayar biçimlendirmesinin doğru olduğundan emin olmak için bu satırı eklemeniz daha iyi:dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
bubuxu

buna nereden seslenmeli?
markhorrocks

Bu benim için Swift 4'te iyi çalıştı (küçük ayarlamalarla)
Frédéric Adda

Bu benim için harika çalışıyor, ancak siteyi yalnızca ikinci kez ziyaret ettiğimde (çerezler ilk kez ayarlanmadığında) - buna kimse rastladı mı?
MrChrisBarker

İlk yükleme hata verir ikinci yükleme işi :( sorun ne olabilir?
Shauket Sheikh

10

tanımlama bilgisi ayarla

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

çerezi sil

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

9

Swift 3 güncellemesi:

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
Merhaba, çerezleri de almak için kod ekleyebilir misiniz HTTPCookieStorage.shared?
markhorrocks

Bu, web görünümü tarafından yapılan her isteğe çerez eklemek için WKWebView almamın tek yoludur
Chicowitz

Eğer cevap olarakhttponly cookie içeriyorsa, cookie değerini bu şekilde alamazsınız.
brain

1
bu sadece çerezleri httpcookies deposuna geri yerleştiriyor, burada çerezleri wkwebview için ayarlayan kod nerede?
Shauket Sheikh

8

Burada çeşitli yanıtları inceledikten ve herhangi bir başarı elde edemedikten sonra, WebKit belgelerini taradım ve bir dizi tanımlama bilgisini başlık alanına uygun bir biçime dönüştüren requestHeaderFieldsstatik yönteme rastladım HTTPCookie. Bunu mattr'ınURLRequest çerez başlıklarıyla yüklemeden önce güncelleme anlayışıyla birleştirmek beni bitiş çizgisinden geçirdi.

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

Bunu daha da basitleştirmek için bir uzantı kullanın:

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

Şimdi sadece şöyle olur:

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

Bu uzantı, yalnızca bir açılır çözüm istiyorsanız LionheartExtensions'ta da mevcuttur . Şerefe!


1
@ShauketSheikh hmm, bu hangi durumlarda işe yaramıyor?
Dan Loewenherz

simulator ios 8 kullanarak test ettim, çerez göndermiyor gibi görünüyor. iki kez kontrol ettim.
Shauket Sheikh

cevabımı gönderdim, deneyebilirsiniz @Dan
Shauket Sheikh

7

İOS 11'de artık çerezi yönetebilirsiniz :), bu oturuma bakın: https://developer.apple.com/videos/play/wwdc2017/220/

görüntü açıklamasını buraya girin


2
@ShobhakarTiwari neden? iOS11 resmi sürümünde herhangi bir değişiklik oldu mu?
Jacky

Yalnızca iOS 11 ve sonraki sürümleri destekliyorsanız, daha önceki sürümleri desteklemeniz gerekiyorsa, sayfa yüklenmeden önce JavaScript kullanın.
PashaN

Bu benim için işe yarıyor, ancak bazen setcookie yönteminin tamamlama işleyicisini ÇALIŞTIRMAMASI, yani bazen web sayfam yüklenmiyor - yalnızca cihazda oluyor, 3 / 4'üncü / 5'inci kez kapanma ve yeniden açma oluyor web görünümü ve bir kez olduktan sonra, uygulamayı sıfırlayana kadar devam ediyor - buna kimse rastladı mı?
Binya Koatz

5

Bu cevabı göndermenin arkasındaki sebep, birçok çözümü denedim ama kimse düzgün çalışmıyor, cevabın çoğu ilk seferde çerezi ayarlamanız gerektiğinde ve sonuç çerezinin ilk seferde senkronize edilmemesi durumunda çalışmıyor, Lütfen bu çözümü kullanın, her ikisi için de işe yarıyor iOS> = 11.0 <= iOS 11 ila 8.0, ilk seferde çerez senkronizasyonuyla da çalışır.

İOS> = 11.0 - Swift 4.2 için

Http çerezlerini alın ve wkwebview çerez deposunda bu şekilde ayarlayın, isteğinizi wkwebview'e yüklemek çok zor bir nokta , çerezler tamamen kurulduğunda yükleme isteği göndermelisiniz, işte yazdığım işlev.

Tamamlandığında kapanma işlevini çağırın, yük web görünümünü çağırın. Bilginize bu işlev yalnızca iOS> = 11.0'ı işler

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

SyncCookies işlevi için uygulama burada .

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

İOS 8'den iOS 11'e kadar

WKUserScript kullanarak iki seferlik tanımlama bilgisi ayarlamak için ihtiyaç duyduğunuz bazı ekstra şeyleri ayarlamanız gerekir ve istek üzerine tanımlama bilgileri eklemeyi de unutmayın, aksi takdirde tanımlama bilginiz ilk seferde senkronize olmaz ve sayfanızın ilk seferinde düzgün yüklenmediğini görürsünüz. iOS 8.0 için çerezleri desteklediğini bulduğum halt bu

Wkwebview nesne oluşturmadan önce.

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


}

Bu işleve odaklanın 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
}

İşte diğer adım wkuserscript çerezleri hemen senkronize etmiyor, çerezle ilk kez sayfa yüklemek için çok fazla şey var, bir işlemi sonlandırırsa web görünümünü yeniden yüklemektir, ancak onu kullanmanızı önermiyorum, kullanıcı bakış açısı için iyi değil , heck, istek başlığına istek seti çerezlerini yüklemeye hazır olduğunuzda ve bu şekilde, iOS sürüm kontrolü eklemeyi unutmayın. yükleme talebinden önce bu işlevi çağırın.

request?.addCookies()

URLRequest için uzantı yazdım

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

        }
    }

  }
}

artık iOS> 8'i test etmeye hazırsınız


2

Lütfen sizin için en uygun çözümü bulun. Temel olarak değiştirilmiş ve Swift 4 için güncellenir @ user3589213 'ın cevabı .

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

Yukarıdaki tüm cevapları denedim ama hiçbiri işe yaramıyor. Pek çok denemeden sonra sonunda WKWebview çerezini ayarlamanın güvenilir bir yolunu buldum.

Öncelikle, bir WKProcessPool örneği oluşturmanız ve WkWebview'in kendisini başlatmak için kullanılacak olan WKWebViewConfiguration'a ayarlamanız gerekir:

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

WKProcessPool'u ayarlamak buradaki en önemli adımdır. WKWebview, süreç izolasyonunu kullanır - bu, uygulamanızın sürecinden farklı bir süreçte çalıştığı anlamına gelir. Bu bazen çatışmaya neden olabilir ve çerezinizin WKWebview ile düzgün şekilde senkronize edilmesini engelleyebilir.

Şimdi WKProcessPool'un tanımına bakalım

Bir web görünümüyle ilişkili işlem havuzu, web görünümü yapılandırmasıyla belirlenir. Her web görünümüne, uygulama tanımlı bir işlem sınırına ulaşılana kadar kendi Web İçeriği süreci verilir; bundan sonra, aynı işlem havuzuna sahip web görünümleri, Web İçeriği işlemlerini paylaşır.

Alt sıra talepleri için aynı WKWebview'u kullanmayı planlıyorsanız, son cümleye dikkat edin.

Aynı işlem havuzuna sahip web görünümleri, Web İçeriği işlemlerini paylaşır

Demek istediğim, aynı etki alanı için bir WKWebView'ı her yapılandırdığınızda aynı WKProcessPool örneğini kullanmıyorsanız (belki bir WKWebView içeren bir VC A'nız vardır ve farklı yerlerde farklı VC A örnekleri oluşturmak istiyorsanız) ), çakışan tanımlama bilgileri olabilir. Sorunu çözmek için, B etki alanını yükleyen bir WKWebView için WKProcessPool'un ilk oluşturulmasından sonra, onu bir tekil olarak kaydediyorum ve aynı etki alanını yükleyen bir WKWebView oluşturmak zorunda olduğum her seferde aynı WKProcessPool'u kullanıyorum 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
}()

Başlatma işleminin ardından, bir URLRequest'i yükleyebilir tamamlama blok içinde arasında httpCookieStore.setCookie. Burada, çerezi istek başlığına eklemelisiniz, aksi takdirde çalışmaz.

P / s: Uzantıyı Dan Loewenherz'in yukarıdaki fantastik yanıtından çaldım

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'in cevabının benim versiyonum. Üzerinde test edildi iOS 11, 12, 13. Senin gibi görünüyor kullanmak gerekmez DispatchGroupüzerinde iOS 13artık.

Statik olmayan işlevi includeCustomCookiesaçık olarak kullanıyorum WKWebViewConfiguration, böylece cookiesher yeni oluşturduğumda güncelleme yapabilirim WKWebViewConfiguration.

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

O zaman bunu şöyle kullanıyorum:

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 istekleri için daha iyi düzeltme burada gösterilmektedir

Swift 4 versiyonu:

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 kullanan biri varsa, bu daha iyi bir çözümdür.

  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

Bu benim için çalışıyor: setcookies'den sonra fetchdatarecords ekleyin

   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

Çoklu çerez öğeleri eklerken, bunu şu şekilde yapabilirsiniz: ( path& domainher öğe için gereklidir)

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

aksi takdirde, yalnızca ilk çerez öğesi ayarlanacaktır.


0

UIWebView'dan HTTPCookieStorage'a benzer davranışlar elde etmek için WKWebsiteDataStore'u da kullanabilirsiniz.

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

0

Aşağıdaki kod, Swift5 projemde iyi çalışıyor. aşağıdaki WKWebView ile url yüklemeyi deneyin:

    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

Bu, iOS 9 veya sonraki sürümlerde Çerezler ve WKWebView ile başa çıkmak için benim çözümüm.

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

Yaptığım bu hata, url'nin tamamını domain özniteliğinde geçiriyordum, sadece alan adı olmalı.

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

webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.