답변:
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를 사용하여 문서 시작시 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];
이 두 가지 기술을 결합하면 쿠키 값을 Native App Land에서 Web View Land로 전송할 수있는 충분한 도구가 제공됩니다. 고급 쿠키가 더 필요한 경우 Mozilla 페이지 의 쿠키 자바 스크립트 API에서 자세한 정보를 찾을 수 있습니다 .
예, 애플이 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...
이렇게하면 다른 도메인의 공유 저장소에서 쿠키를 보내지 않고 안전하지 않은 요청으로 보안 쿠키를 보내지 않고도 첫 번째 요청에 올바른 쿠키가 설정됩니다.
또한 다른 요청에 쿠키가 설정되어 있는지 확인해야합니다. 이는 쿠키 세트가 있는지 확인하고 쿠키 세트가 없는지 확인하는 쿠키를 사용하여 문서로드시 실행되는 스크립트를 사용하여 수행됩니다 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"];
변경된 쿠키를 업데이트하기 위해 delegate 메소드를 구현하여 현재 도메인에서만 쿠키를 업데이트하고 있는지 확인하십시오.
- (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;
}
a=b
쿠키 문자열로 끝날 것입니다 name=a=b;domain=.example.com;path=/
-표준이 분할 된 ;
다음 키 = 값 쌍에서 첫 번째 로 분할 된다고 생각합니다 =
. 나는 이것을 테스트 할 것이다 :)
쿠키는 쿠키를 WKWebView
생성 하기 전에 구성에서 설정해야합니다 . 그렇지 않으면 WKHTTPCookieStore
의 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
}
}
위의 예제는 마지막 순간까지보기 생성을 지연시킵니다. 또 다른 솔루션은 구성 또는 웹보기를 미리 작성하고보기 컨트롤러를 작성하기 전에 비동기 특성을 처리하는 것입니다.
마지막 참고 사항 :이 웹보기를 만든 후에는 야생으로 느슨하게 설정하면 이 답변에 설명 된 방법을 사용하지 않고 더 많은 쿠키를 추가 할 수 없습니다 . 그러나 WKHTTPCookieStoreObserver
API를 사용하여 최소한 쿠키 변경 사항을 관찰 할 수 있습니다 . 따라서 웹뷰에서 세션 쿠키가 업데이트 HTTPCookieStorage
되면 원하는 경우이 새 쿠키로 시스템을 수동으로 업데이트 할 수 있습니다 .
이에 대한 자세한 내용은이 2017 WWDC 세션 사용자 정의 웹 컨텐츠로드 에서 18:00로 건너 뛰십시오 . 이 세션을 시작할 때 웹뷰가 완료 핸들러에서 작성되어야한다는 사실을 생략하는기만적인 코드 샘플이 있습니다.
cookieStore.setCookie(cookie!) {
webView.load(loggedInURLRequest)
}
18:00의 라이브 데모가이를 명확하게 보여줍니다.
최소한 Mojave Beta 7 및 iOS 12 Beta 7부터 편집 하면 쿠키와 훨씬 일관된 동작이 나타납니다. 이 setCookie(_:)
방법 WKWebView
은 쿠키를 만든 후 쿠키를 설정하는 것으로 보입니다 . 내가하는 것이 중요하지만를 발견했다 만지지processPool
전혀 변수를. 쿠키 설정 기능은 추가 풀이 생성되지 않고 해당 속성이 단독으로 남아있을 때 가장 잘 작동합니다. 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 에서 모든 쿠키를 주입하는 Swift 의 Mattrs 솔루션 버전입니다 . 이것은 주로 사용자 쿠키를 생성하기 위해 인증 쿠키를 주입하기 위해 수행되었습니다.
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()
}
스위프트 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 설명서를 살펴보고 requestHeaderFields
정적 메소드를 우연히 발견했습니다. HTTPCookie
이 쿠키 배열을 헤더 필드에 적합한 형식으로 변환합니다. 이것을 업데이트하는 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에서는 쿠키를 관리 할 수 있습니다 :), https://developer.apple.com/videos/play/wwdc2017/220/
이 답변 뒤에 숨겨진 이유는 많은 솔루션을 시도했지만 아무도 제대로 작동하지 않기 때문에 쿠키를 처음 설정해야하고 결과 쿠키가 처음으로 동기화되지 않은 경우 대부분의 답변이 작동하지 않기 때문에이 솔루션을 모두 사용하십시오. iOS> = 11.0 <= 8.0부터 iOS 11까지, 쿠키 동기화와 함께 처음으로 작동합니다.
iOS> = 11.0 -Swift 4.2
가져 오기 HTTP 쿠키 와의 세트 wkwebview의 이 방법으로 같은 쿠키의 저장, 그것은 귀하의 요청을로드하는 매우 까다로운 점이다 wkwebview은 여기, 쿠키가 완전히 설정 될 거 할 때, 반드시로드에 대한 요청을 보낸 내가 쓴 기능입니다.
폐쇄 가 완료된 상태에서 호출 기능 로드 웹뷰를 호출합니다. 참고 로이 기능은 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 11에서 iOS 8까지
WKUserScript 를 사용하여 두 개의 시간 쿠키를 하나씩 설정하는 데 필요한 추가 사항을 설정해야합니다. 하며 요청시 쿠키를 추가하는 것을 잊지 마십시오. 그렇지 않으면 쿠키가 처음으로 동기화되지 않으므로 처음으로 페이지가 제대로로드되지 않습니다. 이것은 iOS 8.0 용 쿠키를 지원하는 것으로 판명되었습니다.
Wkwebview 객체 생성 전에
func setUpWebView() {
let userController: WKUserContentController = WKUserContentController.init()
if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
if let cookies = HTTPCookieStorage.shared.cookies {
if let script = getJSCookiesString(for: cookies) {
cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userController.addUserScript(cookieScript!)
}
}
}
let webConfiguration = WKWebViewConfiguration()
webConfiguration.processPool = BaseWebViewController.processPool
webConfiguration.userContentController = userController
let customFrame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 0.0, height: self.webContainerView.frame.size.height))
self.WwebView = WKWebView (frame: customFrame, configuration: webConfiguration)
self.WwebView.translatesAutoresizingMaskIntoConstraints = false
self.webContainerView.addSubview(self.WwebView)
self.WwebView.uiDelegate = self
self.WwebView.navigationDelegate = self
self.WwebView.allowsBackForwardNavigationGestures = true // A Boolean value indicating whether horizontal swipe gestures will trigger back-forward list navigations
self.WwebView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .trailing, relatedBy: .equal, toItem: self.webContainerView, attribute: .trailing, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .leading, relatedBy: .equal, toItem: self.webContainerView, attribute: .leading, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .top, relatedBy: .equal, toItem: self.webContainerView, attribute: .top, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .bottom, relatedBy: .equal, toItem: self.webContainerView, attribute: .bottom, multiplier: 1, constant: 0))
}
이 함수에 초점을 맞 춥니 다. getJSCookiesString
public func getJSCookiesString(for cookies: [HTTPCookie]) -> String? {
var result = ""
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
for cookie in cookies {
if cookie.name == "yout_cookie_name_want_to_sync" {
result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
if let date = cookie.expiresDate {
result += "expires=\(dateFormatter.string(from: date)); "
}
if (cookie.isSecure) {
result += "secure; "
}
result += "'; "
}
}
return result
}
여기에 쿠키를 즉시 동기화하지 않는 다른 단계 wkuserscript가 있습니다. 쿠키 하나를 사용하여 처음 페이지를로드하는 데 많은 시간이 필요합니다. 이런 식으로 요청 헤더에 요청 세트 쿠키를로드 할 준비가 될 때마다 iOS 버전 확인을 추가하는 것을 잊지 마십시오. 로드 요청 전에이 함수를 호출하십시오.
request?.addCookies()
URLRequest에 대한 확장 프로그램을 작성했습니다
extension URLRequest {
internal mutating func addCookies() {
//"appCode=anAuY28ucmFrdXRlbi5yZXdhcmQuaW9zLXpOQlRTRmNiejNHSzR0S0xuMGFRb0NjbUg4Ql9JVWJH;rpga=kW69IPVSYZTo0JkZBicUnFxC1g5FtoHwdln59Z5RNXgJoMToSBW4xAMqtf0YDfto;rewardadid=D9F8CE68-CF18-4EE6-A076-CC951A4301F6;rewardheader=true"
var cookiesStr: String = ""
if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
let mutableRequest = ((self as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
if let yourCookie = "YOUR_HTTP_COOKIE_OBJECT" {
// if have more than one cookies dont forget to add ";" at end
cookiesStr += yourCookie.name + "=" + yourCookie.value + ";"
mutableRequest.setValue(cookiesStr, forHTTPHeaderField: "Cookie")
self = mutableRequest as URLRequest
}
}
}
}
이제 iOS> 8을 테스트 할 준비가되었습니다.
즉시 사용 가능한 솔루션을 찾으십시오. 기본적으로 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 쿠키를 설정하는 신뢰할 수있는 방법을 찾았습니다.
먼저 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는 프로세스 격리를 사용합니다. 즉, 앱 프로세스와 다른 프로세스에서 실행됩니다. 때때로 충돌이 발생하여 쿠키가 WKWebview와 제대로 동기화되지 않을 수 있습니다.
이제 WKProcessPool 의 정의를 보자
웹보기와 연관된 프로세스 풀은 웹보기 구성에 의해 지정됩니다. 각 웹보기에는 구현 정의 프로세스 제한에 도달 할 때까지 자체 웹 컨텐츠 프로세스가 제공됩니다. 그 후 동일한 프로세스 풀이있는 웹보기는 결국 웹 컨텐츠 프로세스를 공유합니다.
하위 시퀀스 요청에 동일한 WKWebview를 사용하려는 경우 마지막 문장에주의하십시오.
동일한 프로세스 풀이있는 웹보기는 결국 웹 컨텐츠 프로세스를 공유합니다.
의미하는 바는 동일한 도메인에 대해 WKWebView를 구성 할 때마다 동일한 WKProcessPool 인스턴스를 사용하지 않으면 WKWebView를 포함하는 VC A가 있고 다른 위치에 VC A의 다른 인스턴스를 작성하려는 경우입니다. ), 쿠키 설정 충돌이있을 수 있습니다. 문제를 해결하기 위해 도메인 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
. 여기서 쿠키를 요청 헤더에 첨부해야합니다. 작동하지 않습니다.
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)
})
}
XHR 요청에 대한 더 나은 수정은 다음과 같습니다.
스위프트 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))
}
})
}
곱하기 쿠키 항목을 추가 할 때 다음과 같이 할 수 있습니다 : ( 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];
그렇지 않으면 첫 번째 쿠키 항목 만 설정됩니다.
아래 코드는 내 프로젝트 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 이상에서 쿠키 및 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)