I am migrating from UIWebView to WKWebView and facing problem in cookies management. As app need to support iOS 10 I am not adding cookies through WKHTTPCookieStore instead, I am following the below code:
- (void)loadURL:(NSString *)url
{
NSMutableURLRequest * req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* cookies = [storage cookiesForURL:[NSURL URLWithString:url]];
NSMutableString *cookieStr = [NSMutableString string];
for (NSHTTPCookie *cookie in cookies) {
if (cookieStr.length) {
[cookieStr appendString:#"; "];
}
[cookieStr appendFormat:#"%#=%#", cookie.name, cookie.value];
}
NSLog(#"Cookie: %#",cookieStr);
[req addValue:cookieStr forHTTPHeaderField:#"Cookie"];
NSMutableString *cookieStrAfterDocLoad = [NSMutableString string];
for (NSHTTPCookie *cookie in cookies) {
[cookieStrAfterDocLoad appendFormat:#"document.cookie = '%#=%#';", cookie.name, cookie.value];
}
WKUserScript *userScript = [[WKUserScript alloc] initWithSource: cookieStrAfterDocLoad
injectionTime: WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[self.wkWebView.configuration.userContentController addUserScript:userScript];
[self.wkWebView loadRequest:req];
}
I need to add the cookies after the document load to maintain the session for internal navigation.
This is not the correct way as the new cookies may be added or old may be modified at the time of page load. This needs to be done in many other controllers.
My question is how can I know if the cookies were added at every for loadRequst in UIWebview, or is there any other correct way to handle this situation in WKWebview as some webpage load might not need the cookies?
Is this the correct way to add cookies for every loadRequest?
Related
I'm trying to share WKWebView Cookies with UIWebView for getting all cookies. I know its very simple to get all cookies from UIWebView as compare WKWebView.
I create two WebView's (WKWebView, UIWebView) in Tabbed Application Template. Below method that i am using for share WKWebView cookies with UIWebView but didn't get success.
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
let response = navigationResponse.response as? HTTPURLResponse
let cookies = HTTPCookie.cookies(withResponseHeaderFields: response?.allHeaderFields as! [String : String], for: (response?.url)!)
HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always
for cookie in cookies {
HTTPCookieStorage.shared.setCookie(cookie)
}
decisionHandler(WKNavigationResponsePolicy.allow);
}
Using above code when i Login Into my account from WKWebView, UiWebView didn't Login me already.
I also tried to share UIWebView Cookies With WKWebView, and it worked.
Please can anyone tell me how i can share WKWebView Cookies With UIWebView or how i can get all cookies from WKWebView?
Thanks
You need to set the wkwebview cookies by following the steps below
first in the initialization is to execute the script to set up Cookies:
-(void)initWebView
{
WKWebViewConfiguration *webViewconfiguration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
if(URL.host hasSuffix:baseDomain){
//Here to determine whether the domain name is their own website
NSString *jScript = [self setCookies];
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[wkUController addUserScript:wkUScript];
}
webViewconfiguration.userContentController = wkUController;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, width, height) configuration:webViewconfiguration];
}
//The script that executes may be slightly different
+(NSString *)setCookies
{
NSString *script = [NSString string];
for (NSHTTPCookie *httpCookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
{
NSString *cookie = [NSString stringWithFormat:#"%#=%#;domain=%#;path=%#",httpCookie.name,httpCookie.value,httpCookie.domain,httpCookie.path?:#"/"];
script = [script stringByAppendingString:[NSString stringWithFormat:#"document.cookie='%#';",cookie]];
}
return script;
}
And then manually add cookies to the header of the HTTP when creating the NSURLRequest object:
- (void)loadRequest:(NSURLRequest *)request
{
//Here to determine whether the domain name is their own website
if ([request.URL.host hasSuffix:baseDomain]){
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL];
NSString *cookies = #""; //setting Cookies
[mutableRequest addValue:cookies forHTTPHeaderField:#"Cookie"];
}
// do request
}
This is really painful to setup cookies and load webview, it's tricky to set cookies before loading webview. if you set cookies in just a delegate method it never load page first time. you must take care of cookies are done before loading page. you can ceck my answer in other question, but it is in swift but you can follow the same technique in objective c. Can I set the cookies to be used by a WKWebView?
I need to read cookies from Safari app to inside the my application. So is there any way to do it by using any SFSafariViewController inside the our application. Please help me on it.
Thanks in advance.
Edited
I have a one URL https://example.com/abc123 and when I open this URL in Safari application it sets some cookies automatically. Now I have to open my application and read those cookies by passing the domain (https://example.com).
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:[NSURL URLWithString:#"https://wwwexample.com"]];
Is it possible to read cookies?
According to the documentation, there's no way you can read cookies using SFSafariViewController. You need to use WKWebView or UIWebView for that. For using UIWebView to retrieve cookies, you can use it delegate method:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSHTTPCookieStorage *cookiesStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURLRequest *urlReq = webView.request;
NSURL *url = urlReq.URL;
NSArray *cookies = [cookiesStorage [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url]];
}
I know I can use the below code to change the default agent:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"Your user agent", #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
but I want to request a different User-Agent for different requests. Examples: request 'www.google.com', use agent=‘google Agent’, request 'www.github.com', use agent='github Agent'.
I have tried the below way to set the 'User_Agent' in each request, but it doesn't seem to work.
- (void)viewDidLoad {
NSString *urlAddress = #"http://www.amazon.com";
//Create a URL object.
NSURL *url = [NSURL URLWithString:urlAddress];
//URL Requst Object
NSMutableURLRequest *requestObj = [NSMutableURLRequest requestWithURL:url];
[requestObj setValue:#"Foobar/1.0" forHTTPHeaderField:#"User_Agent"];
// But the value is not "Foobar/1.0"
NSString* secretAgent = [webView stringByEvaluatingJavaScriptFromString:#"navigator.userAgent"];
NSLog(#"agent - %#", secretAgent);
//Load the request in the UIWebView.
[webView loadRequest:requestObj];
}
One more question:
It seems a change to the default agent only work change in 'AppDelegate' or 'initialize'. Am I right? Because I try to change in 'viewDidLoad' but it doesn't seem to work.
I observed that if you load any url in webView with a user-agent set
to it. After some time you load the different url & different user
agent but used the same allocated WebView instane.The web view will
load the new url but can not set new user-agent. It just set old one
that you have set before loading first time.
You can not change untill you initiate new instanse of UIWebview(or
we can say that user agent can be set once per session). So I coame to result that user-agent can be set only once.
What I did for this problem is, I allocated new instance when I want set new user-agent.
You don't need to set it using JavaScript.
You have misspelled User-Agent. Use this minimal code taken from here
NSString* userAgent = #"My Cool User Agent";
NSURL* url = [NSURL URLWithString:#"http://whatsmyuseragent.com/"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
I am working on an app that needs to download a web page from our iis based website. If I am logged into the domain for my wireless connection on my iPad the site I am connecting to seems to use that login for my credentials. However, if I am not connected to the domain or I am connected as a user who does not have access to the page it does fire the didReceiveAuthenticationChallenge, otherwise it does not. If I use Safari to connect to the same page it asks for authentication regardless. I am hoping to get the app to authenticate each time. Any help would be appreciated.
Code to request page :
NSError *error = nil;
// assign the cmh url from user prefs
NSURL *url = [NSURL URLWithString:cmhUrl];
// Put that URL into an NSURLRequest
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
// Create a connection that will exchange this request for data from the URL
connection = [[NSURLConnection alloc] initWithRequest:req
delegate:self
startImmediately:YES];
It looks like when you authenticate in Safari the auth-token is saved in NSHTTPCookieStorage. And when you making request from the code, all cookies from the storage are added to the header, so there is no need to ask for a token again.
Try to clear the storage before you making the request:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookieStorage.cookies) {
[cookieStorage deleteCookie:cookie];
}
Hope this helps.
I am using NSURLCredentialPersistenceForSession within didReceiveAuthenticationChallenge of NSURLConnection delegate method while login.
Now when I logout and use this code for clearing the storage..
NSURLCredentialStorage *credentialStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *credentialsDicationary = [credentialStorage allCredentials];
NSLog(#"credentialsDicationary..%#",[credentialsDicationary description]);
for (NSURLProtectionSpace *space in [credentialsDicationary allKeys]) {
NSDictionary *spaceDictionary = [credentialsDicationary objectForKey:space];
NSLog(#"spaceDictionary..%#",[spaceDictionary description]);
for (id userName in [spaceDictionary allKeys]) {
NSURLCredential *credential = [spaceDictionary objectForKey:userName];
[credentialStorage removeCredential:credential forProtectionSpace:space];
}
}
But when I suddenly login again exactly after logout the login happens with wrong credentials. Please let mw know how to clear the cache. It works if I relogin after some 5 secs of time.
Thanks in advance..
AJ
If you're using NSURLSession, invalidate the session and create a new one, e.g.:
[self.session invalidateAndCancel];
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
This will clear session-scoped credentials.
If you use NSURLCredentialPersistenceForSession to create the credential then the app stores the credential for the entire session until the app is closed.
You can work around this by either:
changing the url (like appending '#' to the end of the url)
Use NSURLCredentialPersistenceNone and provide the credentials with each nsurlrequest
Append auth info to the url (http://username:password#mywebsite.com), instead of using creds
Append auth info to the request header, instead of using creds
//Pseudo code for appending to header:
NSString *authString = [NSString stringWithFormat:#"%#:%#", self.loginCreds.username, self.loginCreds.password];
NSData *stringData = [authString dataUsingEncoding:NSUTF8StringEncoding];
authString = [NSString stringWithFormat:#"Basic %#", [stringData base64EncodedString]];
[[self requestHeader] setValue:authString forHTTPHeaderField:#"Authorization"];
It's possible to remove the credential but it can take minutes before it is actually removed. However, I don't remember how its done.. It's either done the way you mentioned in the question or through the keychain.
I had the same issue and I tried clear the credential storage, cookies associated with url and even trying to reset the session but nothing seemed to work, finally I just resorted to adding a a random query string value to the end of the url and that did the trick for me
// Random number calculated.
NSInteger randomNumber = arc4random() % 16;
NSURL* apiURL = [NSURL URLWithString:#"https://localhost/api/"];
[[RKObjectManager sharedManager] getObjectsAtPath:[NSString stringWithFormat:#"%#getUser?randomNumber=%d",apiURL,randomNumber]
parameters:nil
success:successBlock
failure:failureBlock];
So instead of trying to remove current cached credentials (which seemed impossible) just use a "fresh" url so there aren't any cached objects associated with it.
I was facing the same problem, now it works.
Using NSURLConnection this issue can be fixed easily by adding a random number to the end of the URL:
So, search for your URLRequest and append the random number to URLRequest
NSInteger randomNumber = arc4random() % 999;
NSString *requestURL = [NSString stringWithFormat:#"%#?cache=%ld",yourURL,(long)randomNumber];
NSURL *URLRequest = [NSURL URLWithString:requestURL];
And make sure you have a random number at the end of all URLs you are calling.