回答:
iOS 11以降のみの編集
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以前の古い回答
最初のロード要求でCookieを設定する必要がある場合は、NSMutableURLRequestで設定できます。Cookieは特別にフォーマットされたリクエストヘッダーにすぎないため、次のように実現できます。
WKWebView * webView = /*set up your webView*/
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]];
[request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"];
// use stringWithFormat: in the above line to inject your values programmatically
[webView loadRequest:request];
ページの後続のAJAXリクエストでCookieを設定する必要がある場合は、WKUserScriptを使用して、ドキュメントの開始時に次のようにjavascriptを介してプログラムで値を設定するだけで実現できます。
WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc]
initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// again, use stringWithFormat: in the above line to inject your values programmatically
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];
これら2つの手法を組み合わせると、Cookieの値をネイティブアプリランドからWebビューランドに転送するのに十分なツールが得られます。さらに高度なCookieが必要な場合は、MozillaのページでCookieのJavaScript APIに関する詳細をご覧ください。
ええ、AppleがUIWebViewの多くの優れた点をサポートしていないのは残念です。彼らが彼らをサポートするかどうかはわかりませんが、うまくいけば彼らはすぐにこれに乗り込むでしょう。お役に立てれば!
この回答で遊んだ後(これは非常に役に立ちました:)私たちはいくつかの変更を加える必要がありました:
NSHTTPCookieStorage
そのため、コードを次のように変更しました。
NSMutableURLRequest *request = [originalRequest mutableCopy];
NSString *validDomain = request.URL.host;
const BOOL requestIsSecure = [request.URL.scheme isEqualToString:@"https"];
NSMutableArray *array = [NSMutableArray array];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
// Don't even bother with values containing a `'`
if ([cookie.name rangeOfString:@"'"].location != NSNotFound) {
NSLog(@"Skipping %@ because it contains a '", cookie.properties);
continue;
}
// Is the cookie for current domain?
if (![cookie.domain hasSuffix:validDomain]) {
NSLog(@"Skipping %@ (because not %@)", cookie.properties, validDomain);
continue;
}
// Are we secure only?
if (cookie.secure && !requestIsSecure) {
NSLog(@"Skipping %@ (because %@ not secure)", cookie.properties, request.URL.absoluteString);
continue;
}
NSString *value = [NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value];
[array addObject:value];
}
NSString *header = [array componentsJoinedByString:@";"];
[request setValue:header forHTTPHeaderField:@"Cookie"];
// Now perform the request...
これにより、共有ストレージから他のドメイン用のCookieを送信したり、安全なCookieを安全でないリクエストに送信したりせずに、最初のリクエストに正しいCookieが設定されます。
また、他のリクエストにCookieが設定されていることを確認する必要もあります。これは、ドキュメントのロード時に実行されるスクリプトを使用して行われます。このスクリプトは、Cookieが設定されているかどうかを確認し、設定されていない場合はの値に設定しNSHTTPCookieStorage
ます。
// Get the currently set cookie names in javascriptland
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
// Skip cookies that will break our script
if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
continue;
}
// Create a line that appends this cookie to the web view's document's cookies
[script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptString];
}
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[userContentController addUserScript:cookieInScript];
...
// Create a config out of that userContentController and specify it when we create our web view.
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;
self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config];
また、Cookieの値を変更するサーバーにも対処する必要があります。つまり、を更新するために作成しているWebビューからコールバックする別のスクリプトを追加しますNSHTTPCookieStorage
。
WKUserScript *cookieOutScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.updateCookies.postMessage(document.cookie);"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[userContentController addUserScript:cookieOutScript];
[userContentController addScriptMessageHandler:webView
name:@"updateCookies"];
デリゲートメソッドを実装して変更されたCookieを更新し、現在のドメインのCookieのみが更新されるようにします。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSArray<NSString *> *cookies = [message.body componentsSeparatedByString:@"; "];
for (NSString *cookie in cookies) {
// Get this cookie's name and value
NSArray<NSString *> *comps = [cookie componentsSeparatedByString:@"="];
if (comps.count < 2) {
continue;
}
// Get the cookie in shared storage with that name
NSHTTPCookie *localCookie = nil;
for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:self.wk_webView.URL]) {
if ([c.name isEqualToString:comps[0]]) {
localCookie = c;
break;
}
}
// If there is a cookie with a stale value, update it now.
if (localCookie) {
NSMutableDictionary *props = [localCookie.properties mutableCopy];
props[NSHTTPCookieValue] = comps[1];
NSHTTPCookie *updatedCookie = [NSHTTPCookie cookieWithProperties:props];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:updatedCookie];
}
}
}
これにより、WKWebViewを使用する場所ごとに異なる方法で対処しなくても、Cookieの問題が解決するようです。これで、このコードをヘルパーとして使用してWebビューを作成し、透過的に更新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;
}
a=b
あなたであったなら、あなたはクッキー文字列で終わるでしょうname=a=b;domain=.example.com;path=/
-私は標準が分割し;
、次にkey = valueペアの最初のもの =
で分割すると信じています。私もこれをテストします:)
をWKWebView
作成する前に、構成でCookieを設定する必要があります。それ以外の場合は、さえでWKHTTPCookieStore
のsetCookie
完了ハンドラ、クッキーは確実にWeb表示に同期されません。これは上のドキュメントからこの行に戻りますWKWebViewConfiguration
@NSCopying var configuration: WKWebViewConfiguration { get }
それ @NSCopying
はやや深いコピーです。実装は私を超えていますが、最終結果は、webviewを初期化する前にcookieを設定しない限り、そこにあるcookieを信頼できません。ビューの初期化は非同期プロセスになるため、アプリのアーキテクチャが複雑になる可能性があります。あなたはこのようなものになるでしょう
extension WKWebViewConfiguration {
/// Async Factory method to acquire WKWebViewConfigurations packaged with system cookies
static func cookiesIncluded(completion: @escaping (WKWebViewConfiguration?) -> Void) {
let config = WKWebViewConfiguration()
guard let cookies = HTTPCookieStorage.shared.cookies else {
completion(config)
return
}
// Use nonPersistent() or default() depending on if you want cookies persisted to disk
// and shared between WKWebViews of the same app (default), or not persisted and not shared
// across WKWebViews in the same app.
let dataStore = WKWebsiteDataStore.nonPersistent()
let waitGroup = DispatchGroup()
for cookie in cookies {
waitGroup.enter()
dataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
}
waitGroup.notify(queue: DispatchQueue.main) {
config.websiteDataStore = dataStore
completion(config)
}
}
}
そしてそれを次のように使う
override func loadView() {
view = UIView()
WKWebViewConfiguration.cookiesIncluded { [weak self] config in
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.load(request)
self.view = webView
}
}
上記の例は、可能な限り最後までビューの作成を延期します。別の解決策は、configまたはwebviewを事前に作成し、非同期の性質を処理してからビューコントローラーを作成することです。
最後の注意:このWebビューを作成した後、それを実際に使用するように設定すると、この回答で説明されている方法を使用せずにCookieを追加することはできません。ただし、WKHTTPCookieStoreObserver
APIを使用して、少なくともCookieに発生する変更を監視できます。したがって、セッションCookieがWebViewで更新されたHTTPCookieStorage
場合、必要に応じて、この新しいCookieでシステムを手動で更新できます。
詳しくは、2017年のWWDCセッションのカスタムWebコンテンツの読み込みで 18:00までスキップしてください。このセッションの始めに、webviewが完了ハンドラーで作成されるべきであるという事実を省略している不正なコードサンプルがあります。
cookieStore.setCookie(cookie!) {
webView.load(loggedInURLRequest)
}
18:00のライブデモはこれを明らかにします。
編集少なくともMojaveベータ7およびiOS 12ベータ7以降、Cookieでの動作の一貫性が大幅に向上しています。setCookie(_:)
この方法は、さらに後にクッキーを設定できるように表示されますWKWebView
作成されています。私がする、それが重要かかわらを見つけた触れないでprocessPool
すべての変数を。Cookie設定機能は、追加のプールが作成されておらず、そのプロパティがそのままにされている場合に最適に機能します。WebKitのいくつかのバグが原因で問題が発生していたと言っても差し支えないと思います。
私のために働く
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)
}
}
else
条件で彼はdecisionHandler
クロージャを呼び出している.cancel
ので、webview
は実際には初期リクエストをロードしません。条件でloadRequest
が呼び出された後、else
このデリゲートメソッドはその要求に対して再度呼び出されif
、Cookie
ヘッダーが存在するため条件に入れられます。
else
状態になることはないため、これは機能しません。
HTTPCookieStorageからすべてのCookieを注入するためのSwift のMattrsソリューションの私のバージョンは次のとおりです。これは、主に認証Cookieを挿入してユーザーセッションを作成するために行われました。
public func setupWebView() {
let userContentController = WKUserContentController()
if let cookies = HTTPCookieStorage.shared.cookies {
let script = getJSCookiesString(for: cookies)
let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(cookieScript)
}
let webViewConfig = WKWebViewConfiguration()
webViewConfig.userContentController = userContentController
self.webView = WKWebView(frame: self.webViewContainer.bounds, configuration: webViewConfig)
}
///Generates script to create given cookies
public func getJSCookiesString(for cookies: [HTTPCookie]) -> String {
var result = ""
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
for cookie in cookies {
result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
if let date = cookie.expiresDate {
result += "expires=\(dateFormatter.stringFromDate(date)); "
}
if (cookie.secure) {
result += "secure; "
}
result += "'; "
}
return result
}
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
クッキーを設定する
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()
}
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)
}
}
HTTPCookieStorage.shared
か?
ここでさまざまな回答を調べても成功しなかった後、WebKitのドキュメントを調べて、cookieの配列をヘッダーフィールドに適した形式に変換するrequestHeaderFields
上の静的メソッドを見つけましたHTTPCookie
。これとCookieヘッダーを読み込む前にを更新するというmattrの洞察を組み合わせるとURLRequest
、フィニッシュラインを通過することができました。
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でも利用できます。、ドロップインソリューションだけが必要な場合。乾杯!
iOS 11では、今すぐcookieを管理できます:)。このセッションを参照してください:https : //developer.apple.com/videos/play/wwdc2017/220/
この回答を投稿した理由は、多くの解決策を試したが、誰も正しく機能しなかったためです。ほとんどの回答は、最初にCookieを設定する必要がある場合に機能せず、結果としてCookieが最初に同期されませんでした。この解決策を両方で機能するように使用してくださいiOS> = 11.0 <= iOS 11から8.0まで。Cookieの同期も初めて機能します。
iOSの場合> = 11.0 -Swift 4.2
この方法でhttp cookieを取得し、wkwebview cookieストアに設定します。リクエストをロードするのは非常にトリッキーです。 wkwebview。cookieが完全に設定されるときにロード要求を送信する必要があります。ここに書いた関数があります。
load 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を使用して、2つのタイムCookieを1つ設定するために必要な追加の設定を行う必要があります。、リクエストでCookieを追加することも忘れないでください。そうしないと、Cookieが最初に同期されず、ページが初めて正しく読み込まれないことがわかります。これは、iOS 8.0のCookieをサポートしていることがわかりました
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がすぐにcookieを同期しない場合があります。cookieを使用して最初のページをロードするには、プロセスを終了する場合はwebviewを再度リロードする必要がありますが、使用はお勧めしません。ユーザーの観点からはあまり良くありません。このように、リクエストヘッダーにリクエストセットCookieをロードする準備ができているときはいつでも、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をテストする準備ができました
箱から出してすぐに動作する可能性が最も高いソリューションを見つけてください。基本的には、Swift 4 @ user3589213の回答に合わせて変更および更新されています。
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)
}
}
上記のすべての答えを試しましたが、どれもうまくいきません。何度も試みた結果、WKWebview Cookieを設定するための信頼できる方法がようやく見つかりました。
まず、WKProcessPoolのインスタンスを作成し、それをWkWebview自体の初期化に使用されるWKWebViewConfigurationに設定する必要があります。
private lazy var mainWebView: WKWebView = {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.processPool = WKProcessPool()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
return webView
}()
WKProcessPoolの設定は、ここで最も重要なステップです。WKWebviewはプロセスの分離を利用します。つまり、アプリのプロセスとは異なるプロセスで実行されます。これにより、競合が発生し、CookieがWKWebviewと正しく同期されなくなることがあります。
次に、WKProcessPoolの定義を見てみましょう
Webビューに関連付けられたプロセスプールは、Webビューの構成で指定されます。各Webビューには、実装定義のプロセス制限に達するまで、独自のWebコンテンツプロセスが与えられます。その後、同じプロセスプールを持つWebビューは、Webコンテンツプロセスを共有することになります。
サブシーケンスリクエストに同じWKWebviewを使用する場合は、最後の文に注意してください
同じプロセスプールを持つWebビューは、最終的にWebコンテンツプロセスを共有します
つまり、同じドメインにWKWebViewを構成するたびにWKProcessPoolの同じインスタンスを使用しない場合(おそらく、WKWebViewを含むVC Aがあり、異なる場所にVC Aの異なるインスタンスを作成したいということです) )、Cookieの設定が競合している可能性があります。この問題を解決するには、ドメインBをロードするWKWebViewのWKProcessPoolを最初に作成した後、それをシングルトンに保存し、同じドメインBをロードするWKWebViewを作成する必要があるたびに同じWKProcessPoolを使用します。
private lazy var mainWebView: WKWebView = {
let webConfiguration = WKWebViewConfiguration()
if Enviroment.shared.processPool == nil {
Enviroment.shared.processPool = WKProcessPool()
}
webConfiguration.processPool = Enviroment.shared.processPool!
webConfiguration.processPool = WKProcessPool()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
return webView
}()
初期化プロセスが完了したら、URLRequestのをロードすることができ、完了ブロック内のhttpCookieStore.setCookie
。ここでは、リクエストヘッダーにCookieを添付する必要があります。そうしないと機能しません。
P / s: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)
}
}
私のバージョンのnteissの答え。でテスト済みiOS 11, 12, 13
。あなたのようなルックスは、使用する必要はありませんDispatchGroup
でiOS 13
、もう。
私は非静的関数を使用するincludeCustomCookies
上でWKWebViewConfiguration
、私は更新できるようにすることを、cookies
私は新しい作成するたびに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()
}
}
}
次に、次のように使用します。
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)
})
}
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)
}
誰かが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)
}
これは私のために働きます: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))
}
})
}
乗算Cookieアイテムを追加するときは、次のようにすることができます(各アイテムにはpath
&domain
が必要です)
NSString *cookie = [NSString stringWithFormat:@"document.cookie = 'p1=%@;path=/;domain=your.domain;';document.cookie = 'p2=%@;path=/;domain=your.domain;';document.cookie = 'p3=%@;path=/;domain=your.domain;';", p1_string, p2_string, p3_string];
WKUserScript *cookieScript = [[WKUserScript alloc]
initWithSource:cookie
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
それ以外の場合は、最初のCookieアイテムのみが設定されます。
以下のコードは、私のプロジェクトSwift5でうまく機能しています。以下のWKWebViewでURLをロードしてみてください。
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)
}
これは、iOS 9以降でCookieとWKWebViewを処理するための私のソリューションです。
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!)
}
}
私が行っていたこの間違いは、ドメイン属性でURL全体を渡していたため、ドメイン名のみである必要があります。
let cookie = HTTPCookie(properties: [
.domain: "example.com",
.path: "/",
.name: "MyCookieName",
.value: "MyCookieValue",
.secure: "TRUE",
])!
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)