Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Cookies don't work at all right after first launch, but work afterwards #247

Open
pwbs opened this issue May 30, 2016 · 41 comments
Open

Cookies don't work at all right after first launch, but work afterwards #247

pwbs opened this issue May 30, 2016 · 41 comments

Comments

@pwbs
Copy link

pwbs commented May 30, 2016

Hi, I'm trying building an (iOS) app based on WkWebView.
On the first launch, when I use the (web-based) login form, it doesn't work at all, all cookies are dropped!

After I "pause" the app (by hitting the Home button) or after it's been killed, I can use the login form and it works normally. (Well, almost normally, since only one cookie out of 4 is kept, but it's the only mandatory one...)

This behaviour, with my code, is fully reproductible, I've spent about 2 days (full-time) on this, trying to make things work. If I use UIWebView instead, it works fine (except that there are the weird things that come with UI, such as the scrolling events that are not triggered properly). If I use apache/cordova-plugin-wkwebview-engine, it's the exact same broken behaviour.

I'm gonna fall back to UIWebView until I find a fix for it. But I'm not fully giving up yet.

Any help, any clue, would be very appreciated!

@bezzer
Copy link

bezzer commented Jun 2, 2016

I have experienced this issue as well on first launch after installing from the App Store or Testflight. For me this only occurs on first install, not on updates.

So far I haven't found a way to work around it without making the login/join forms work without cookies, which is probably a bigger pain than it's worth.

@pwbs
Copy link
Author

pwbs commented Jun 2, 2016

Same here, it's only the first launch after the first installation. When it's updated, it doesn't have the problem (unless there are some specific changes perhaps, I'm not sure).

Login/Join forms not working on the first launch after the first install is a major blocker for me. :(

Anyway, thank you @bezzer for reassuring me (and my teammates) on the fact that we're not the only one with this issue! (I have not found anything via Google on this particular matter so far.)

@pwbs
Copy link
Author

pwbs commented Jun 2, 2016

According to what I understand and guess from http://stackoverflow.com/a/33198799, it might be a problem of synchronising the cookies between the web view and the rest... ôÔ

@pwbs
Copy link
Author

pwbs commented Jun 10, 2016

This bug has been there for more than a year in https://bugs.webkit.org/show_bug.cgi?id=140191

I finally gave up on trying to fix this bug. I'm now using a work-around, which consists in using localStorage to store those cookie stuff.

@bezzer
Copy link

bezzer commented Aug 15, 2016

Realise this may be a bit late @pwbs, but I finally got a chance to look into this more closely, and was able to force the cookies to be stored by copying them from the server response into NSHTTPCookieStorage in the WKNavigationDelegate decidePolicyForNavigationResponse implementation.

NSHTTPURLResponse * resp = (NSHTTPURLResponse*)[navigationResponse response];
NSDictionary  *diction = [resp allHeaderFields];

NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:diction forURL:[resp URL]];

for (NSHTTPCookie *cookie in cookies) {
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}

return decisionHandler(YES);

Cookies work on first install after this change for me, but it feels a bit heavy handed to copy them on every response.

@JulianLaval
Copy link

JulianLaval commented Jan 17, 2017

Experiencing this as well! Any thoughts on implementing the snippet as a plugin to rectify the issue?

@chanphillip
Copy link

Got the same issue.
@bezzer Could you explain how should I implement that workaround?

@bezzer
Copy link

bezzer commented Jan 23, 2017

@chanphillip you would need to add the decidePolicyForNavigationResponse implementation after MyMainViewController.m line 603. I would Create a pull request, but my app used a different WKWebviewPlugin so couldn't test the fix.

@chanphillip
Copy link

@bezzer Thanks for your help. But unfortunately I couldn't make it work on my end.
In fact I'm using another wkwebview as well (cordova-plugin-wkwebview-engine).
I tried adding your code but did not work. Cookies is still not set at first launch after clean install.

@chanphillip
Copy link

Is decidePolicyForNavigationResponse suppose to be fired every time it receives ajax response?

@agarcia17
Copy link

any update on this?

@hobzcalvin
Copy link

@bezzer @chanphillip decidePolicyForNavigationResponse isn't called for ajax responses, so it doesn't work for me. Doesn't seem to apply to in-app browser responses either. The only time it's ever called in my app is for the cordova root file. e.g. file:///var/containers/Bundle/Application/XXX-XXX-XXX/MyApp.app/www/index.html
My root file uses XHRs to authenticate, and those XHRs don't store cookies properly unless the app is backgrounded and then foregrounded, even with @bezzer's fix. So, still not a fix for me.

@hobzcalvin
Copy link

I came up with a workaround.

@psirenny
Copy link

@hobzcalvin does this workaround stop working if the cookies on the server change? I'm thinking of applying your hack but I'm worried that the client side cookies that are synced up the first time will eventually becomes stale.

Does anyone know why the heck this only happens on the first install? Is this related to Apple's attempts to thwart cookie tracking?

@hobzcalvin
Copy link

hobzcalvin commented Oct 24, 2017 via email

@XavierLost
Copy link

I've been tortured by the WKWebView.I'm going to go back to UIWebView.

@imransilvake
Copy link

Any work around that works with this cordova-plugin-wkwebview-engine ?

@ryanlin1986
Copy link

Still not fixed?

@imransilvake
Copy link

nope

@CWBudde
Copy link

CWBudde commented Jun 11, 2018

If anyone is interested in some backgrounds of this problem you may find this answer on SO very interesting: https://stackoverflow.com/a/49534854/2757879
With this in mind I could make it to work by injecting a dummy cookie (foo=bar) for my domain right before the webview was initialized. After this has been done once the mentioned syncing works reliable ever after.

@CWBudde
Copy link

CWBudde commented Jun 12, 2018

Based on the mentioned mechanism in my previous post, I have developed an additional plugin that allows to inject a dummy cookie to get the sync process started properly. While this will only work in iOS11 (and above) it solves the issue sufficiently for my client.
The repo can be found here: https://github.com/CWBudde/cordova-plugin-wkwebview-inject-cookie

@imransilvake
Copy link

workaround: with the help of different people, I fixed the problem. here is the cordova plugin that might help someone else too:
https://github.com/imransilvake/Cordova-Plugin-Sync-Cookies

@ryanlin1986
Copy link

@imransilvake Thanks so much, although this solution may not acceptable by user experience for waiting too long

@imransilvake
Copy link

@ryanlin1986 yes, I agree with that. Unless and until Apple fixes this bug, we have to use this workaround, unfortunately.
At least this timeout is for the very first run of the app and not for the rest of the runs.

@CWBudde
Copy link

CWBudde commented Jun 16, 2018

@ryanlin1986 As mentioned above you could directly inject the cookie, then you don't have to wait for the extra request and for the cookie to get synced properly. It works immediately. Though it's iOS 11 only (which has a market share of about 80%). If you need to support older devices you could add the other workaround as well. Then you only have the delay for older devices.

@shauket
Copy link

shauket commented Sep 24, 2018

If anyone is interested in some backgrounds of this problem you may find this answer on SO very interesting: https://stackoverflow.com/a/49534854/2757879
With this in mind I could make it to work by injecting a dummy cookie (foo=bar) for my domain right before the webview was initialized. After this has been done once the mentioned syncing works reliable ever after.

but he does not mention for work around ios 8,9,10

@CWBudde
Copy link

CWBudde commented Sep 24, 2018

Basically for older iOS the problem is the same, but the workaround doesn't work immediately. It takes some time (or further action like minimizing the app) until the cookies are sync'ed properly.
In case of our app we display a message on the very first start like this: "The app is prepared, please wait a moment". Then in a regular interval we check if the cookies work, otherwise we force the setting again until it works. This sometimes takes about 20-30 seconds and it's pretty ugly, but it works and I guess that people using older iOS versions already get used to a slow loading as long as it works.

@shauket
Copy link

shauket commented Sep 24, 2018 via email

@shauket
Copy link

shauket commented Sep 25, 2018

FYI

i wrote my answer that is best and no need to make thread to wait for 20 or 30 seconds , or minimize your app or something that is wired behavior for users, please check my answer on stackoverflow.

https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/52490200#52490200

@matodrobec
Copy link

@pw374
Copy link

pw374 commented Oct 4, 2018

We have to do downgrade https://ionicframework.com/docs/wkwebview/#downgrading-to-uiwebview

Yeah... no, no one wants to go back to UIwebview... performance sucks, semantics suck, mostly everything with it sucks, except cookies work better, but it shouldn't be worth the rest of the crap!

@matodrobec
Copy link

HI @pw374 ,
we need more cookies than performance

@pw374
Copy link

pw374 commented Oct 4, 2018

Sad... So, you mean you can't configure your server to use local storage in place of cookies, on iOS clients?

@matodrobec
Copy link

I would like to use token auth but server app support only cookies :( and ionic native-http is not solve issue with cookie and CORS too :(

@TaLinh
Copy link

TaLinh commented Jan 15, 2019

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.

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

@FarhadG
Copy link

FarhadG commented Feb 11, 2019

After upgrading to WKWebView, we're actually not able to see any cookies at all. I'm surprised that many folks are able to set cookies within their iOS WKWebView. Any tips on how to make this happen?

Before we were using UIWebView, where our auth service would set cookies in our browser (http-only), however, after the upgrade, none of the cookies are read or set.

@TaLinh
Copy link

TaLinh commented Feb 11, 2019

After upgrading to WKWebView, we're actually not able to see any cookies at all. I'm surprised that many folks are able to set cookies within their iOS WKWebView. Any tips on how to make this happen?

Before we were using UIWebView, where our auth service would set cookies in our browser (http-only), however, after the upgrade, none of the cookies are read or set.

Can you show some code or elaborate what you have so far?

@FarhadG
Copy link

FarhadG commented Feb 13, 2019

Sure thing, @TaLinh! Here's a snippet of code:

I'm making a simple request with the following:

// index.js inside of Cordova application
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', function(evt) {
  console.log({ evt, response: this.response });
});

xhr.open('GET', 'http://localhost:8000/api');
xhr.send();

And here's the mockserver attaching cookies to every request:

// server.js
const cors = require('cors');
const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();

app.use(cookieParser());
app.use(cors());

app.get('*', (req, res) => {
  res.cookie('username', 'john doe', { maxAge: 900000, httpOnly: true });
  res.send('Hello World!');
});

Now when I inspect the DevTools, I can see the Set-Cookies in the response but I don't see the cookies actually set in the browser.

screen shot 2019-02-13 at 2 11 02 pm

screen shot 2019-02-13 at 2 11 11 pm

@TaLinh
Copy link

TaLinh commented Feb 14, 2019

I don't see anything wrong with the code snippet above. Have you tried my approach above for injecting cookies into WKWebview? This is how I construct a cookie to use for my webView:

let cookie = HTTPCookie(properties: [
            .domain: "your_domain",
            .path: "/",
            .name: "your_auth_token_field_name",
            .value: "your_auth_token_value"
            ])!

@mzaidkhan
Copy link

mzaidkhan commented Sep 14, 2019

Guys, I was having the same problem with my app using WKWebView on IOS 12.4

I came across your post when I was struggling to find a solution but your solutions did not work for me. Still grateful to you guys for guiding me in the right direction. 👍🏼

After a lot of trial and error, I just hit the hammer on the head and found that all I had to do was to load the WebView initially with the host url and it started to work without any problems.

Code below:

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

var webView: WKWebView!
let screenSize: CGRect = UIScreen.main.bounds
let hostURL: String = "REPLACE_WITH_HOST_URL" // as in https://mycompany.com 

var firstTime: Bool = true
@IBOutlet weak var myView: UIView!

func loadUrl(oURL:String){
    if let url = URL(string: oURL) {
        var request = URLRequest(url: url)
        request.httpShouldHandleCookies = true // not required it is by default true
        self.webView.load(request)
    }
}
private func setupWebView(iURL: String) {
    let configuration = WKWebViewConfiguration()
    configuration.processPool = WKProcessPool()
    let contentController = WKUserContentController()
    configuration.userContentController = contentController
    self.webView = WKWebView(frame : myView.frame , configuration : configuration)
    self.webView.isUserInteractionEnabled = true
    self.webView.allowsBackForwardNavigationGestures = true
    self.webView.navigationDelegate = self
    self.webView.uiDelegate = self
    self.view.addSubview(self.webView)
    self.loadUrl(oURL: iURL)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if self.firstTime {
        self.setupWebView(iURL: self.hostURL)
        self.firstTime = false
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
}

Hope it is helpful for you guys as well.

@tajinder-logiciel
Copy link

I am facing a similar issue, cookies in my project are not working in ionic-1 ios apps with wkwebview
Does anyone have any solution?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests