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?
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 am developing one iPad application using rest kit After successfully login the web service returns some cookies .
[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] returns an array of cookies. I need to save this array of cookies and reuse after the application restart without call the web service.I tried KeyChain for save the cookies but that time application crashed.
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
NSMutableArray *myArray=[[NSMutableArray alloc]init];
NSData *encodedData= [NSKeyedArchiver archivedDataWithRootObject:myArray];
NSString *encodedString=[[NSString alloc]initWithData:encodedData encoding:NSUTF8StringEncoding];
[keychainItem setObject:encodedString forKey:(__bridge id)(kSecValueData)];
NSString *decodedString=[keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSData *decodedData= [decodedString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *decodedArray =[NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
I follow above code to store cookies in keychain But the encoding String reruns Null values.
Then I tried userdefault method that time also my crashed shows the error." Attempt to set a non-property-list object as an NSUserDefaults value for key "
Someone help to save the cookies in properly and reuse with rest kit after application restart
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 have a business need to be able to customize the UserAgent for an embedded UIWebView. (For instance, I'd like the server to respond differently if, say, a user is using one version of the app versus another.)
Is it possible to customize the UserAgent in the existing UIWebView control the way it is, say, for an embedded IE browser in a Windows app?
Modern Swift
Here's a suggestion for Swift 3+ projects from StackOverflow users PassKit and Kheldar:
UserDefaults.standard.register(defaults: ["UserAgent" : "Custom Agent"])
Source: https://stackoverflow.com/a/27330998/128579
Earlier Objective-C Answer
With iOS 5 changes, I recommend the following approach, originally from this StackOverflow question: UIWebView iOS5 changing user-agent as pointed out in an answer below. In comments on that page, it appears to work in 4.3 and earlier also.
Change the "UserAgent" default value by running this code once when
your app starts:
NSDictionary *dictionary = #{#"UserAgent": #"Your user agent"};
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
[[NSUserDefaults standardUserDefaults] synchronize];
See previous edits on this post if you need methods that work in versions of iOS before 4.3/5.0. Note that because of the extensive edits, the following comments / other answers on this page may not make sense. This is a four year old question, after all. ;-)
I had this problem too, and tried all methods. I found that only this method works (iOS 5.x):
UIWebView iOS5 changing user-agent
The principle is to set the user agent permanently in the user settings. This works; Webview sends the given header. Just two lines of code:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"Mozilla/Whatever version 913.6.beta", #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
Setting User-Agent, or User_Agent in the mutable request, or overriding the setValue in the NSHttpRequest by swizzling, - I tried all that and controlled the results with wireshark, and none of that seems to work, because Webview still uses the user agent value from the user defaults, no matter what you try to set in the NSHttpRequest.
It should work with an NSMutableURLRequest as Kuso has written.
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"http://www.google.com/"]];
[urlRequest setValue: #"iPhone" forHTTPHeaderField: #"User-Agent"]; // Or any other User-Agent value.
You'll have to use NSURLConnection to get the responseData. Set the responseData to your UIWebView and the webView should render:
[webView loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL];
Very simple in Swift. Just place the following into your App Delegate.
UserDefaults.standard.register(defaults: ["UserAgent" : "Custom Agent"])
If you want to append to the existing agent string then:
let userAgent = UIWebView().stringByEvaluatingJavaScript(from: "navigator.userAgent")! + " Custom Agent"
UserDefaults.standard.register(defaults: ["UserAgent" : userAgent])
Note: You may will need to uninstall and reinstall the App to avoid appending to the existing agent string.
Actually adding any header field to the NSURLRequest argument in shouldStartLoadWithRequest seems to work, because the request responds to setValue:ForHTTPHeaderField - but it doesn't actually work - the request is sent out without the header.
So I used this workaround in shouldStartLoadWithRequest which just copies the given request to a new mutable request, and re-loads it. This does in fact modify the header which is sent out.
if ( [request valueForHTTPHeaderField:#"MyUserAgent"] == nil )
{
NSMutableURLRequest *modRequest = [request mutableCopyWithZone:NULL];
[modRequest setValue:#"myagent" forHTTPHeaderField:#"MyUserAgent"];
[webViewArgument loadRequest:modRequest];
return NO;
}
Unfortunately, this still doesn't allow overriding the user-agent http header, which is apparently overwritten by Apple. I guess for overriding it you would have to manage a NSURLConnection by yourself.
Using #"User_Agent" simply causes a custom header to appear in the GET request.
User_agent: Foobar/1.0\r\nUser-Agent: Mozilla/5.0 (iPhone; U; CPU
iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like
Gecko) Mobile/7D11\r\n
The above is what appears in the dissected HTTP packet, essentially confirming what Sfjava was quoting from that forum. It's interesting to note that "User-Agent" gets turned into "User_agent."
Taking everything this is how it was solved for me:
- (void)viewDidLoad {
NSURL *url = [NSURL URLWithString: #"http://www.amazon.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"Foobar/1.0" forHTTPHeaderField:#"User-Agent"];
[webView loadRequest:request];
}
Thanks Everyone.
By pooling the answer by Louis St-Amour and the NSUserDefaults+UnRegisterDefaults category from this question/answer, you can use the following methods to start and stop user-agent spoofing at any time while your app is running:
#define kUserAgentKey #"UserAgent"
- (void)startSpoofingUserAgent:(NSString *)userAgent {
[[NSUserDefaults standardUserDefaults] registerDefaults:#{ kUserAgentKey : userAgent }];
}
- (void)stopSpoofingUserAgent {
[[NSUserDefaults standardUserDefaults] unregisterDefaultForKey:kUserAgentKey];
}
This solution seems to have been seen as a pretty clever way to do it
changing-the-headers-for-uiwebkit-http-requests
It uses Method Swizzling and you can learn more about it on the CocoaDev page
Give it a look !
I faced the same question. I want to add some info to the user-agent, also need to keep the original user-agent of webview. I solved it by using the code below:
//get the original user-agent of webview
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
NSString *oldAgent = [webView stringByEvaluatingJavaScriptFromString:#"navigator.userAgent"];
NSLog(#"old agent :%#", oldAgent);
//add my info to the new agent
NSString *newAgent = [oldAgent stringByAppendingString:#" Jiecao/2.4.7 ch_appstore"];
NSLog(#"new agent :%#", newAgent);
//regist the new agent
NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:newAgent, #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];
Use it before you instancing webview.
Try this in the AppDelegate.m
+ (void)initialize
{
// Set user agent (the only problem is that we can’t modify the User-Agent later in the program)
// iOS 5.1
NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:#”Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3”, #”UserAgent”, nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];
}
The only problem I have found was change user agent only
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *dictionary = [NSDictionary
dictionaryWithObjectsAndKeys:
#"Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
#"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
}
Apple will soon stop accepting apps with UIWebView. Find below for how you could change the user agent in WKWebView.
let config = WKWebViewConfiguration()
config.applicationNameForUserAgent = "My iOS app"
webView = WKWebView(frame: <the frame you need>, configuration: config)
To just add a custom content to the current UserAgent value, do the following:
1 - Get the user agent value from a NEW WEBVIEW
2 - Append the custom content to it
3 - Save the new value in a dictionary with the key UserAgent
4 - Save the dictionary in standardUserDefaults.
See the exemple below:
NSString *userAgentP1 = [[[UIWebView alloc] init] stringByEvaluatingJavaScriptFromString:#"navigator.userAgent"];
NSString *userAgentP2 = #"My_custom_value";
NSString *userAgent = [NSString stringWithFormat:#"%# %#", userAgentP1, userAgentP2];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:userAgent, #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];