I'm trying to switch an existing app from UIWebView
to WKWebView
. The current app manages the users login / session outside of the webview
and sets the cookies
required for authentication into the the NSHTTPCookieStore
. Unfortunately new WKWebView
doesn't use the cookies
from the NSHTTPCookieStorage
. Is there another way to achieve this?

- 239
- 1
- 4

- 2,300
- 3
- 16
- 16
25 Answers
Edit for iOS 11+ only
Use 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)
Since you are pulling them over from HTTPCookeStorage, you can do this:
let cookies = HTTPCookieStorage.shared.cookies ?? []
for cookie in cookies {
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
}
Old answer for iOS 10 and below
If you require your cookies to be set on the initial load request, you can set them on NSMutableURLRequest. Because cookies are just a specially formatted request header this can be achieved like so:
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];
If you require subsequent AJAX requests on the page to have their cookies set, this can be achieved by simply using WKUserScript to set the values programmatically via javascript at document start like so:
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];
Combining these two techniques should give you enough tools to transfer cookie values from Native App Land to Web View Land. You can find more info on the cookie javascript API on Mozilla's page if you require some more advanced cookies.
Yeah, it sucks that Apple is not supporting many of the niceties of UIWebView. Not sure if they will ever support them, but hopefully they will get on this soon.
-
1Where is the best place to inject cookies for subsequent requests? E.g. The initial page load is covered in the answer above, but what if there are links on the page that also lead to the same domain, and also need the same cookies injected into the request? didStartProvisionalNavigation? – Mason G. Zhwiti May 04 '15 at 16:34
-
you will see two examples in this answer. the second example is what you are looking for: "If you require subsequent AJAX requests on the page to have their cookies set, this can be achieved by...". I should probably remove the term "AJAX" because it is not exclusive to javascript. – mattr May 04 '15 at 16:52
-
@mattr Strange, I am already using that technique (thanks, btw), and it does work for ajax requests happening on the page. But when the user clicks a link on the page, the cookies are not being passed on that subsequent request. Any ideas? – Mason G. Zhwiti May 04 '15 at 17:07
-
1sorry it is not working for you. In my mind, as long as the domains are the same there shouldn't be any issue. Can you double check in the code that the link is pointing to the same domain that you loaded the request from? Also, cookies can be constrained to a specific "path" as well. Maybe that is causing some issues? – mattr May 04 '15 at 17:31
-
@mattr OK this is likely the issue. The initial page is (for example) alpha.mydomain.com, and the links being clicked on are beta.mydomain.com. Normally we just set the cookie domain to .mydomain.com, so they both work OK. But it's not clear to me yet how to accomplish the same scenario in WKWebView-land. – Mason G. Zhwiti May 04 '15 at 17:38
-
@mattr Awesome! Never realized domain could be set on javascript cookie commands. This fixed it, maybe should mention this in the answer above? Thanks again. – Mason G. Zhwiti May 04 '15 at 17:50
-
11Note that the javascript technique to set the cookies will not work for "HTTP Only" cookies. – Ahmed Nasser Mar 20 '16 at 18:13
-
1The above method works great... but i could see cookies duplicated in the subsequent AJAX calls(Duplicated only once). – Durga Vundavalli Jun 14 '16 at 12:15
-
I finally got it working by also adding the cookie to the shared NSHTTPCookieStorage like so: [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; .... Why, OH WHY, Apple does it have to be so difficult to work with you?! – holm50 Dec 05 '16 at 13:17
-
what's the content of TeskCookieKey/TeskCookieValue? is there an example to show? – ikzjfr0 Mar 20 '17 at 03:34
-
I have to load an HTML string via loadData. Can I still set a cookie if I don't use loadRequest? Would your WKUserScript code cover this? – Chicowitz Apr 07 '17 at 03:26
-
@AhmedNasser do you know any workaround for this? thanks – Axel92Dev May 15 '17 at 14:05
-
1@Axel92Dev a workaround would be to make sure the first request made from your webview to your server gets a response that explicitly tells the webview to set the cookies again with the HTTPOnly flag (ie: set the cookies again in the response). You can make a special API for that sole purpose when initializing the webview, then use the webview normally on success. – Ahmed Nasser May 16 '17 at 17:32
-
@AhmedNasser thank you for your response. Your solution is clean. But i found that my back end services read correctly the cookie even if it is not HTTP Only (in fact it is passed in the Cookie HTTP Request Header) – Axel92Dev May 17 '17 at 07:39
-
@Axel92Dev The solution in the answer works works fine for non-HTTP-only cookies. These are the cookies that are exposed to the JavaScript. Normally, you would not want your sensitive cookies to be exposed to the JavaScript. An example of a sensitive cookie is the SessionId cookie. So you mark this as an HTTP only cookie. But then the solution in the answer will not work. You can add it in Javascript without the HTTP only flag, but this means you are exposing it to the Javascript and you are in a security risk. – Ahmed Nasser May 17 '17 at 08:45
-
1For AJAX it could be not enough to set cookies with this sample JS script: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';" you should also add "path=/;" and "domain=your_domain". If you don't add path then system (for some reason) would set path by itself and it would be the path of your first request. Given that all next requests that have different path will not get your cookies. – SPopenko Aug 30 '17 at 15:11
-
Getting one issue, when cookies was updated by javascript from server side, it never update cookies – Muhammad Shauket Oct 07 '18 at 04:39
-
This works well, but you should set your cookie path while you setup your cookies. – noark Mar 27 '19 at 03:00
-
4`webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)` this is not working for me for ios13, does anyone else facing this issue ? – Faiq Mustaqeem Apr 22 '20 at 12:40
-
@FaiqMustaqeem me to. I still encountering this issue. Does anyone has the perfect solution? – Raditya Kurnianto May 15 '20 at 05:02
-
I'm missing something... the "Old answer for iOS 10 and below" seems to assume that I know the value of the cookies that my app set in the past. But... of course, I don't. How do I actually get those values? – Ben Wheeler Oct 05 '20 at 14:11
-
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie) is settings cookie, but that process is asynchronous. So if you want to set cookie and than load content, you need to use setCookie(cookie, completionHandler) and in completion handler you need to say self.webview.load(url). This way it works as expected. – Juraj Antas May 06 '22 at 19:26
After playing with this answer (which was fantastically helpful :) we've had to make a few changes:
- We need web views to deal with multiple domains without leaking private cookie information between those domains
- We need it to honour secure cookies
- If the server changes a cookie value we want our app to know about it in
NSHTTPCookieStorage
- If the server changes a cookie value we don't want our scripts to reset it back to its original value when you follow a link / AJAX etc.
So we modified our code to be this;
Creating a request
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...
This makes sure that the first request has the correct cookies set, without sending any cookies from the shared storage that are for other domains, and without sending any secure cookies into an insecure request.
Dealing with further requests
We also need to make sure that other requests have the cookies set. This is done using a script that runs on document load which checks to see if there is a cookie set and if not, set it to the value in NSHTTPCookieStorage
.
// Get the currently set cookie names in javascriptland
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
// Skip cookies that will break our script
if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
continue;
}
// Create a line that appends this cookie to the web view's document's cookies
[script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptString];
}
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[userContentController addUserScript:cookieInScript];
...
// Create a config out of that userContentController and specify it when we create our web view.
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;
self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config];
Dealing with cookie changes
We also need to deal with the server changing a cookie's value. This means adding another script to call back out of the web view we are creating to update our 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"];
and implementing the delegate method to update any cookies that have changed, making sure that we are only updating cookies from the current domain!
- (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];
}
}
}
This seems to fix our cookie problems without us having to deal with each place we use WKWebView differently. We can now just use this code as a helper to create our web views and it transparently updates NSHTTPCookieStorage
for us.
EDIT: Turns out I used a private category on NSHTTPCookie - here's the code:
- (NSString *)wn_javascriptString {
NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
self.name,
self.value,
self.domain,
self.path ?: @"/"];
if (self.secure) {
string = [string stringByAppendingString:@";secure=true"];
}
return string;
}

- 38,189
- 13
- 98
- 110
-
6I have wrapped your code into a subclass of WKWebView. Feel free to check it out https://github.com/haifengkao/YWebView – Hai Feng Kao Jun 29 '16 at 05:29
-
What if your cookies contain = signs in the value? Would this work? – iOSAddicted Feb 28 '17 at 13:26
-
@iOSAddicted I _think_ so. If your value was `a=b` you would end up with the cookie string `name=a=b;domain=.example.com;path=/` - I believe that the standard splits on `;` and then splits on the _first_ `=` in the key=value pair. I would test this though :) – deanWombourne Mar 01 '17 at 10:47
-
your response helped me a lot, I would like however to add something to your post, there are several risks when using your update method, some JS frameworks might create cookies that have the same name but different domain, and if you attempt to update it using the js methods you have a high risk of updating a cookie with a wrong value. Also for us the js cookie string, had to be stripped of its secure flag, as our server makes nasty redirects between http and https, causing secure cookies to not be present in some pages in some nasty edge cases. – RicardoDuarte Feb 14 '18 at 09:23
-
I actually think the company I was with when I wrote this did have to add some domain protection to it after it went live. We never (afaik) ran into the secure/unsecure problem - sounds like a nightmare! – deanWombourne Feb 14 '18 at 21:22
-
i dont think we have that sort of thing, so i have to be very careful to not send duplicate cookies or cookies with wrong domain in the requests:(, and a way of doing that is to make sure i preserve the right domain and path always:(, yeah it is a bloody nightmare. Btw did u play with iOS11 cookieStorage, apple hasnt made our lifes any easier with it either:(. – RicardoDuarte Feb 15 '18 at 10:03
-
After testing, I found that if the response of your request (domain A) is 302 forwarding to domain B, your domain A cookies will forward to domain B, which is not expected. – sktree Dec 25 '20 at 05:03
-
The cookies must be set on the configuration before the WKWebView
is created. Otherwise, even with WKHTTPCookieStore
's setCookie
completion handler, the cookies won't reliably be synced to the web view. This goes back to this line from the docs on WKWebViewConfiguration
@NSCopying var configuration: WKWebViewConfiguration { get }
That @NSCopying
is somewhat of a deep copy. The implementation is beyond me, but the end result is that unless you set cookies before initializing the webview, you can't count on the cookies being there. This can complicate app architecture because initializing a view becomes an asynchronous process. You'll end up with something like this
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)
}
}
}
and then to use it something like
override func loadView() {
view = UIView()
WKWebViewConfiguration.cookiesIncluded { [weak self] config in
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.load(request)
self.view = webView
}
}
The above example defers view creation until the last possible moment, another solution would be to create the config or webview well in advance and handle the asynchronous nature before creation of a view controller.
A final note: once you create this webview, you have set it loose into the wild, you can't add more cookies without using methods described in this answer. You can however use the WKHTTPCookieStoreObserver
api to at least observe changes happening to cookies. So if a session cookie gets updated in the webview, you can manually update the system's HTTPCookieStorage
with this new cookie if desired.
For more on this, skip to 18:00 at this 2017 WWDC Session Custom Web Content Loading. At the beginning of this session, there is a deceptive code sample which omits the fact that the webview should be created in the completion handler.
cookieStore.setCookie(cookie!) {
webView.load(loggedInURLRequest)
}
The live demo at 18:00 clarifies this.
Edit As of Mojave Beta 7 and iOS 12 Beta 7 at least, I'm seeing much more consistent behavior with cookies. The setCookie(_:)
method even appears to allow setting cookies after the WKWebView
has been created. I did find it important though, to not touch the processPool
variable at all. The cookie setting functionality works best when no additional pools are created and when that property is left well alone. I think it's safe to say we were having issues due to some bugs in WebKit.

- 1,513
- 15
- 16
-
Looks like cookie handling/setting is more reliable in Mojave 10.14 beta 3 and iOS 12 beta 3 – nteissler Jul 12 '18 at 18:02
-
10
-
2I am still having this issue in iOS 12 with an already loaded WKWebView. Sometimes setCookie() will actually be synced with the WKWebView right away, sometimes it will not making the handling somewhat sporadic – bmjohns Dec 17 '18 at 23:02
-
I've still seen issues too since the radar was claimed fixed, but much less frequently. How frequently are you seeing the cookie failure? If you have a reproducible, small enough project, I'd really recommend submitting a webkit bug here: https://webkit.org/reporting-bugs/ You can also tweet [Brady Eidson](https://twitter.com/bradeeoh) (nicely) a webkit architect at apple who is very responsive to these kinds of reports and bugs. – nteissler Dec 18 '18 at 02:43
-
this is the correct answer - no need to manually translate the cookies as header fields in each URLRequest, it's just that setCookie() needs to be used as described here. – Guillaume Laurent Feb 06 '19 at 11:07
-
This is the only answer that covers the underlying problem (ie you need to init the webview _after_ setting the cookie), and the only answer that solved my problem – Arthur Apr 08 '19 at 05:46
-
Thanks for the answer. Has anyone noticed this behavior? On a simulator, this code only works if you use WKWebsiteDataStore.nonPersistent(), but not with WKWebsiteDataStore.default(). The simulator is running iPhone 11 iOS 13.4. – gokeji May 04 '20 at 04:48
-
underlying problem could be as simple as domain name mismatch. Go check that before trying this solution :) – smile.al.d.way Jul 02 '20 at 04:29
-
This is still an issue on Xcode 11.7 running iOS 13.7. Thanks for your contribution sir! – henrique Oct 28 '20 at 20:09
-
Mostly good answer, however DispatchGroup does not work well in iOS 11ish. Specifically having a device 11.0.3 here and the completion of .setCookie is only called *sometimes*. – Jonny Feb 03 '21 at 10:19
-
This was helpful and this is the way that you can get it working for the initial load. However, has anyone noticed that occasionally your cookies get dropped later on the line? The expiration is set to > 1 year, but sometimes I observe that the cookies just get dropped randomly during the session. I've observed this on several apps I've worked on. – JonathanC Apr 10 '23 at 23:37
work for me
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)
}
}

- 351
- 3
- 3
-
Awesome hack, especially how intransigent iOS is about overriding a cookie in an existing WKWebView. The only gotcha is that the previous WKNavigationKey has become stale. Other code may be waiting in vain on the old one. – BaseZen Apr 02 '17 at 05:38
-
2is this correct? Appreciate it may work in some circumstances. However the responsibility of this delegate method - decidePolicyForNavigationAction - is to decide the policy; not to actually load the request. That has been initiated previously. In which case, does this not cause the request to be loaded twice? – Max MacLeod Aug 17 '17 at 08:42
-
2@MaxMacLeod In the `else` condition he's calling the `decisionHandler` closure with `.cancel` so the `webview` doesn't actually load the initial request. After the `loadRequest` is called in the `else` condition this delegate method will be called again for that request and it will go into the `if` condition because the `Cookie` header will be present. – halil_g Aug 23 '17 at 09:18
-
2Though this wouldn't work when the initial request already has some cookies set, as it will never go into the `else` condition. – halil_g Aug 23 '17 at 09:19
-
Note that this is 1) Doesn't work for every situation - for example, case when web view loads frames 2) Not safe - it could send cookie with sensitive information to 3rd party URL – Peter Prokop Jan 05 '18 at 13:49
-
For normal conditions, it will work...But if we do have internal calls inside the page then it the above logic will treat it as a separate request and will be loaded as a new URL instead of the internal. – Mohit Kumar Mar 28 '18 at 07:16
-
@user3589213 I believe (i'm still unsure but the issue sounds the same) currently experiencing this issue with my Ionic1/AngularJS app. Where would you put this code? In the `AppDelegate.m`? – J.Do Feb 06 '19 at 15:48
Here is my version of Mattrs solution in Swift for injecting all cookies from HTTPCookieStorage. This was done mainly to inject an authentication cookie to create a user session.
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
}

- 5,905
- 4
- 56
- 71

- 693
- 5
- 14
-
better to add this line to make sure the locale formatting is correct: `dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")` – bubuxu Jun 17 '16 at 08:26
-
-
-
This works great for me, but only the second time I visit the site (first time the cookies are not set) - anyone come across this? – MrChrisBarker May 01 '18 at 12:39
-
First load give error second load work :( what could be the issue ? – Muhammad Shauket Sep 24 '18 at 07:58
-
@MrChrisBarker i am facing same issue , i am facing only issue in ios less than 11 for ios 11 i have other work around. – Muhammad Shauket Sep 24 '18 at 07:59
set cookie
self.webView.evaluateJavaScript("document.cookie='access_token=your token';domain='your domain';") { (data, error) -> Void in
self.webView.reload()
}
delete cookie
self.webView.evaluateJavaScript("document.cookie='access_token=';domain='your domain';") { (data, error) -> Void in
self.webView.reload()
}

- 915
- 10
- 16
-
reload would be call at what point? don't you think this is not user friendly after loading webview you are making reload webview again. – Muhammad Shauket Sep 24 '21 at 08:25
Swift 3 update :
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let urlResponse = navigationResponse.response as? HTTPURLResponse,
let url = urlResponse.url,
let allHeaderFields = urlResponse.allHeaderFields as? [String : String] {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url)
HTTPCookieStorage.shared.setCookies(cookies , for: urlResponse.url!, mainDocumentURL: nil)
decisionHandler(.allow)
}
}

- 15,225
- 5
- 36
- 45

- 445
- 5
- 2
-
1Hi, can you add code to get cookies from `HTTPCookieStorage.shared` as well? – markhorrocks Mar 07 '17 at 14:12
-
This is the only way I got WKWebView to add cookies to every request made by the webview – Chicowitz May 12 '17 at 05:14
-
If it contains httponly cookie in response, you could not get the cookies value in this way. – brain Jun 26 '17 at 03:08
-
1this is only doing set cookies back to httpcookies storage where is the code who setup cookies for wkwebview ? – Muhammad Shauket Sep 24 '18 at 08:08
In iOS 11, you can manage cookie now :), see this session: https://developer.apple.com/videos/play/wwdc2017/220/

- 8,038
- 8
- 41
- 54
-
2@ShobhakarTiwari why? does any changes happen in iOS11 formal release? – LiangWang Sep 25 '17 at 08:26
-
Best way to go if you only support iOS 11 and up, if you need to support previous versions, use JavaScript before page loads. – PashaN Mar 27 '18 at 15:58
-
This works for me, except for the fact that sometimes the setcookie method DOESN'T run its completion handler, meaning sometimes my web page doesn't load - only happens on device, happens the 3rd/4th/5th time closing-and-reopening the webview, and after it happens once, it keeps happening until i reset the app - anybody also run into this? – Binya Koatz Mar 27 '18 at 22:59
After looking through various answers here and not having any success, I combed through the WebKit documentation and stumbled upon the requestHeaderFields
static method on HTTPCookie
, which converts an array of cookies into a format suitable for a header field. Combining this with mattr's insight of updating the URLRequest
before loading it with the cookie headers got me through the finish line.
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)
To make this even simpler, use an extension:
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)
}
}
Now it just becomes:
let request = URLRequest(url: URL(string: "https://example.com/")!)
let webView = WKWebView(frame: self.view.frame)
webView.load(request, with: cookies)
This extension is also available in LionheartExtensions if you just want a drop-in solution. Cheers!

- 10,879
- 7
- 50
- 81
-
1
-
i tested using simulator ios 8 it seems to be not sending cookies. i have double checked it. – Muhammad Shauket Sep 25 '18 at 01:00
-
The reason behind posted this answer is I tried many solution but no one work properly, most of the answer not work in case where have to set cookie first time, and got result cookie not sync first time, Please use this solution it work for both iOS >= 11.0 <= iOS 11 till 8.0, also work with cookie sync first time.
For iOS >= 11.0 -- Swift 4.2
Get http cookies and set in wkwebview cookie store like this way, it's very tricky point to load your request in wkwebview, must sent request for loading when cookies gonna be set completely, here is function that i wrote.
Call function with closure in completion you call load webview. FYI this function only handle iOS >= 11.0
self.WwebView.syncCookies {
if let request = self.request {
self.WwebView.load(request)
}
}
Here is implementation for syncCookies function.
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()
}
}
For iOS 8 till iOS 11
you need to setup some extra things you need to set two time cookies one through using WKUserScript and dont forget to add cookies in request as well, otherwise your cookie not sync first time and you will see you page not load first time properly. this is the heck that i found to support cookies for iOS 8.0
before you Wkwebview object creation.
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))
}
Focus on this function 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
}
Here is other step wkuserscript not sync cookies immediately, there a lot of heck to load first time page with cookie one is to reload webview again if it terminate process but i don't recommend to use it, its not good for user point of view, heck is whenever you ready to load request set cookies in request header as well like this way, don't forget to add iOS version check. before load request call this function.
request?.addCookies()
i wrote extension for 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
}
}
}
}
now you ready to go for testing iOS > 8

- 2,643
- 19
- 40
This mistake i was doing is i was passing the whole url in domain attribute, it should be only domain name.
let cookie = HTTPCookie(properties: [
.domain: "example.com",
.path: "/",
.name: "MyCookieName",
.value: "MyCookieValue",
.secure: "TRUE",
])!
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)

- 20,419
- 10
- 66
- 57
Solution for iOS 10+
Details
- Swift 5.1
- Xcode 11.6 (11E708)
Solution
import UIKit
import WebKit
extension WKWebViewConfiguration {
func set(cookies: [HTTPCookie], completion: (() -> Void)?) {
if #available(iOS 11.0, *) {
let waitGroup = DispatchGroup()
for cookie in cookies {
waitGroup.enter()
websiteDataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
}
waitGroup.notify(queue: DispatchQueue.main) { completion?() }
} else {
cookies.forEach { HTTPCookieStorage.shared.setCookie($0) }
self.createCookiesInjectionJS(cookies: cookies) {
let script = WKUserScript(source: $0, injectionTime: .atDocumentStart, forMainFrameOnly: false)
self.userContentController.addUserScript(script)
DispatchQueue.main.async { completion?() }
}
}
}
private func createCookiesInjectionJS (cookies: [HTTPCookie], completion: ((String) -> Void)?) {
var scripts: [String] = ["var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } )"]
let now = Date()
for cookie in cookies {
if let expiresDate = cookie.expiresDate, now.compare(expiresDate) == .orderedDescending { continue }
scripts.append("if (cookieNames.indexOf('\(cookie.name)') == -1) { document.cookie='\(cookie.javaScriptString)'; }")
}
completion?(scripts.joined(separator: ";\n"))
}
}
extension WKWebView {
func loadWithCookies(request: URLRequest) {
if #available(iOS 11.0, *) {
load(request)
} else {
var _request = request
_request.setCookies()
load(_request)
}
}
}
extension URLRequest {
private static var cookieHeaderKey: String { "Cookie" }
private static var noAppliedcookieHeaderKey: String { "No-Applied-Cookies" }
var hasCookies: Bool {
let headerKeys = (allHTTPHeaderFields ?? [:]).keys
var hasCookies = false
if headerKeys.contains(URLRequest.cookieHeaderKey) { hasCookies = true }
if !hasCookies && headerKeys.contains(URLRequest.noAppliedcookieHeaderKey) { hasCookies = true }
return hasCookies
}
mutating func setCookies() {
if #available(iOS 11.0, *) { return }
var cookiesApplied = false
if let url = self.url, let cookies = HTTPCookieStorage.shared.cookies(for: url) {
let headers = HTTPCookie.requestHeaderFields(with: cookies)
for (name, value) in headers { setValue(value, forHTTPHeaderField: name) }
cookiesApplied = allHTTPHeaderFields?.keys.contains(URLRequest.cookieHeaderKey) ?? false
}
if !cookiesApplied { setValue("true", forHTTPHeaderField: URLRequest.noAppliedcookieHeaderKey) }
}
}
/// https://github.com/Kofktu/WKCookieWebView/blob/master/WKCookieWebView/WKCookieWebView.swift
extension HTTPCookie {
var javaScriptString: String {
if var properties = properties {
properties.removeValue(forKey: .name)
properties.removeValue(forKey: .value)
return properties.reduce(into: ["\(name)=\(value)"]) { result, property in
result.append("\(property.key.rawValue)=\(property.value)")
}.joined(separator: "; ")
}
var script = [
"\(name)=\(value)",
"domain=\(domain)",
"path=\(path)"
]
if isSecure { script.append("secure=true") }
if let expiresDate = expiresDate {
script.append("expires=\(HTTPCookie.dateFormatter.string(from: expiresDate))")
}
return script.joined(separator: "; ")
}
private static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
return dateFormatter
}()
}
Usage
Do not forget to paste the Solution code here
class WebViewController: UIViewController {
private let host = "google.com"
private weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
setupWebView()
}
func setupWebView() {
let cookies: [HTTPCookie] = []
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = .nonPersistent()
configuration.set(cookies: cookies) {
let webView = WKWebView(frame: .zero, configuration: configuration)
/// ..
self.webView = webView
self.loadPage(url: URL(string:self.host)!)
}
}
private func loadPage(url: URL) {
var request = URLRequest(url: url)
request.setCookies()
webView.load(request)
}
}
extension WebViewController: WKNavigationDelegate {
// https://stackoverflow.com/a/47529039/4488252
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if #available(iOS 11.0, *) {
decisionHandler(.allow)
} else {
guard let url = navigationAction.request.url, let host = url.host, host.contains(self.host) else {
decisionHandler(.allow)
return
}
if navigationAction.request.hasCookies {
decisionHandler(.allow)
} else {
DispatchQueue.main.async {
decisionHandler(.cancel)
self.loadPage(url: url)
}
}
}
}
}
Full Sample
Do not forget to paste the Solution code here
import UIKit
import WebKit
class ViewController: UIViewController {
private weak var webView: WKWebView!
let url = URL(string: "your_url")!
var cookiesData: [String : Any] {
[
"access_token": "your_token"
]
}
override func viewDidLoad() {
super.viewDidLoad()
let configuration = WKWebViewConfiguration()
guard let host = self.url.host else { return }
configuration.set(cookies: createCookies(host: host, parameters: self.cookiesData)) {
let webView = WKWebView(frame: .zero, configuration: configuration)
self.view.addSubview(webView)
self.webView = webView
webView.navigationDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor).isActive = true
self.view.rightAnchor.constraint(equalTo: webView.rightAnchor).isActive = true
self.loadPage(url: self.url)
}
}
private func loadPage(url: URL) {
var request = URLRequest(url: url)
request.timeoutInterval = 30
request.setCookies()
webView.load(request)
}
private func createCookies(host: String, parameters: [String: Any]) -> [HTTPCookie] {
parameters.compactMap { (name, value) in
HTTPCookie(properties: [
.domain: host,
.path: "/",
.name: name,
.value: "\(value)",
.secure: "TRUE",
.expires: Date(timeIntervalSinceNow: 31556952),
])
}
}
}
extension ViewController: WKNavigationDelegate {
// https://stackoverflow.com/a/47529039/4488252
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if #available(iOS 11.0, *) {
decisionHandler(.allow)
} else {
guard let url = navigationAction.request.url, let host = url.host, host.contains(self.url.host!) else {
decisionHandler(.allow)
return
}
if navigationAction.request.hasCookies {
decisionHandler(.allow)
} else {
DispatchQueue.main.async {
decisionHandler(.cancel)
self.loadPage(url: url)
}
}
}
}
}
Info.plist
add in your Info.plist transport security setting
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

- 24,482
- 9
- 132
- 127
Please find the solution which most likely will work for you out of the box. Basically it's modified and updated for Swift 4 @user3589213's answer.
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)
}
}

- 3,697
- 25
- 19
I have tried all of the answers above but none of them work. After so many attempts I've finally found a reliable way to set WKWebview cookie.
First you have to create an instance of WKProcessPool and set it to the WKWebViewConfiguration that is to be used to initialize the WkWebview itself:
private lazy var mainWebView: WKWebView = {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.processPool = WKProcessPool()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
return webView
}()
Setting WKProcessPool is the most important step here. WKWebview makes use of process isolation - which means it runs on a different process than the process of your app. This can sometimes cause conflict and prevent your cookie from being synced properly with the WKWebview.
Now let's look at the definition of WKProcessPool
The process pool associated with a web view is specified by its web view configuration. Each web view is given its own Web Content process until an implementation-defined process limit is reached; after that, web views with the same process pool end up sharing Web Content processes.
Pay attention to the last sentence if you plan to use the same WKWebview for subsequence requests
web views with the same process pool end up sharing Web Content processes
what I means is that if you don't use the same instance of WKProcessPool each time you configure a WKWebView for the same domain (maybe you have a VC A that contains a WKWebView and you want to create different instances of VC A in different places), there can be conflict setting cookies. To solve the problem, after the first creation of the WKProcessPool for a WKWebView that loads domain B, I save it in a singleton and use that same WKProcessPool every time I have to create a WKWebView that loads the same domain 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
}()
After the initialization process, you can load an URLRequest inside the completion block of httpCookieStore.setCookie
. Here, you have to attach the cookie to the request header otherwise it won't work.
P/s: I stole the extension from the fantastic answer above by 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)
}
}

- 593
- 6
- 11
My version of nteiss's answer. Tested on iOS 11, 12, 13
. Looks like you don't have to use DispatchGroup
on iOS 13
anymore.
I use non-static function includeCustomCookies
on WKWebViewConfiguration
, so that I can update cookies
every time I create new 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()
}
}
}
Then I use it like this:
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)
})
}

- 15,320
- 6
- 84
- 70
Finally got the solution which is working on ios 11+. Pasting my code here...
extension WKWebViewConfiguration {
static func includeCookie(preferences:WKPreferences, completion: @escaping (WKWebViewConfiguration?) -> Void) {
let config = WKWebViewConfiguration()
guard let cookies = HTTPCookieStorage.shared.cookies else {
completion(config)
return
}
config.preferences = preferences
let dataStore = WKWebsiteDataStore.nonPersistent()
HTTPCookieStorage.shared.cookieAcceptPolicy = .always
DispatchQueue.main.async {
let waitGroup = DispatchGroup()
for cookie in cookies{
waitGroup.enter()
let customCookie = HTTPCookie(properties: [
.domain: cookie.domain,
.path: cookie.path,
.name: cookie.name,
.value: cookie.value,
.secure: cookie.isSecure,
.expires: cookie.expiresDate ?? NSDate(timeIntervalSinceNow: 31556926)
])
if let cookieData = customCookie{
dataStore.httpCookieStore.setCookie(cookieData) {
waitGroup.leave()
}
}
}
waitGroup.notify(queue: DispatchQueue.main) {
config.websiteDataStore = dataStore
completion(config)
}
}
}
}
After setting the cookie in WKWebViewConfiguration, use the same config to load the webview...
WKWebViewConfiguration.includeCookie(preferences: preferences, completion: {
[weak self] config in
if let `self` = self {
if let configuration = config {
webview = WKWebView(frame: self.contentView.bounds, configuration: config)
webview.configuration.websiteDataStore.httpCookieStore.getAllCookies { (response) in
print("")
}
self.contentView.addSubview(webview)
if let filePath = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "packageDetailWevview") {
if let requestUrl = filePath {
let request = URLRequest(url: requestUrl)
webview.load(request)
}
}
}
}
})

- 1,914
- 4
- 28
- 30

- 39
- 2
finally got it working in swift 5.
extension WebController{
func save_cookies(){
let cookieStore = self.webView.configuration.websiteDataStore.httpCookieStore
cookieStore.getAllCookies { (cookies) in
let array = cookies.compactMap { (cookie) -> [HTTPCookiePropertyKey: Any]? in
cookie.properties
}
UserDefaults.standard.set(array, forKey: "cookies")
}
}
func load_cookies(){
// get status from cookies
// cookies are pre-installed from native code.
guard let cookies = UserDefaults.standard.value(forKey: "cookies") as? [[HTTPCookiePropertyKey: Any]] else {
return
}
cookies.forEach { (cookie) in
guard let cookie = HTTPCookie(properties: cookie ) else{return}
let cookieStore = self.webView.configuration.websiteDataStore.httpCookieStore
cookieStore.setCookie(cookie, completionHandler: nil)
}
webView.evaluateJavaScript("checkcookie_delay_1second()", completionHandler: nil)
}
}

- 1,753
- 1
- 18
- 26
The better fix for XHR requests is shown here
Swift 4 version:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Swift.Void) {
guard
let response = navigationResponse.response as? HTTPURLResponse,
let url = navigationResponse.response.url
else {
decisionHandler(.cancel)
return
}
if let headerFields = response.allHeaderFields as? [String: String] {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
cookies.forEach { (cookie) in
HTTPCookieStorage.shared.setCookie(cookie)
}
}
decisionHandler(.allow)
}

- 1,229
- 1
- 12
- 22
If anyone is using Alamofire, then this is better solution.
let cookies = Alamofire.SessionManager.default.session.configuration.httpCookieStorage?.cookies(for: URL(string: BASE_URL)!)
for (cookie) in cookies ?? [] {
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
}

- 5,842
- 5
- 38
- 59
This works for me: After setcookies , add 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))
}
})
}
When adding multiply cookie items, you can do it like this: (path
& domain
is required for each item)
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];
otherwise, only the first cookie item will be set.

- 49
- 2
You can also use WKWebsiteDataStore to get similar behaviour to HTTPCookieStorage from UIWebView.
let dataStore = WKWebsiteDataStore.default()
let cookies = HTTPCookieStorage.shared.cookies ?? [HTTPCookie]()
cookies.forEach({
dataStore.httpCookieStore.setCookie($0, completionHandler: nil)
})

- 75
- 1
- 8
Below code is work well in my project Swift5. try load url by WKWebView below:
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)
}
This is my solution to handle with Cookies and WKWebView in iOS 9 or later.
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!)
}
}

- 378
- 2
- 8
Here is how I am doing this-
call initWebConfig in didFinishLaunchingWithOptions of AppDelegate (or anywhere before creating the WebView) otherwise sometimes Cookies do not sync properly-
func initWebConfig() {
self.webConfig = WKWebViewConfiguration()
self.webConfig.websiteDataStore = WKWebsiteDataStore.nonPersistent()
}
func setCookie(key: String, value: AnyObject, domain: String? = nil, group: DispatchGroup? = nil) {
let cookieProps: [HTTPCookiePropertyKey : Any] = [
.domain: domain ?? "google.com",
.path: "/",
.name: key,
.value: value,
]
if let cookie = HTTPCookie(properties: cookieProps) {
group?.enter()
let webConfig = (UIApplication.shared.delegate as? AppDelegate)?.webConfig
webConfig?.websiteDataStore.httpCookieStore.setCookie(cookie) {
group?.leave()
}
}
}
Where required, set cookies in dispatch group-
let group = DispatchGroup()
self.setCookie(key: "uuid", value: "tempUdid" as AnyObject, group: group)
self.setCookie(key: "name", value: "tempName" as AnyObject, group: group)
group.notify(queue: DispatchQueue.main) {
//Create and Load WebView here
let webConfig = (UIApplication.shared.delegate as? AppDelegate)?.webConfig ?? WKWebViewConfiguration()
//create urlRequest
let webView = WKWebView(frame: .zero, configuration: webConfig)
self.webView.load(urlRequest)
}

- 1,497
- 1
- 18
- 16
-
I found nonPersistent works best and wondering why. Because of the clean & isolation environment? – sktree Dec 25 '20 at 05:10