I'm building an iPhone app that is just a UIWebView of an existing mobile site that has a form-based login. When I login to the mobile site on iPhone Safari, I'm prompted to save my username/password, and it's then autofilled when I go back to the site later.
I'd like to enable the same functionality in the UIWebView, but for the life of me, I can't figure out how to do it. Any ideas?
Solution
Following Michael's basic model (see accepted answer), I was able to get this done. Here's what I did:
SETTING DATA
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; {
//save form data
if(navigationType == UIWebViewNavigationTypeFormSubmitted) {
//grab the data from the page
NSString *username = [self.webView stringByEvaluatingJavaScriptFromString: #"document.myForm.username.value"];
NSString *password = [self.webView stringByEvaluatingJavaScriptFromString: #"document.myForm.password.value"];
//store values locally
[[NSUserDefaults standardUserDefaults] setObject:username forKey:#"username"];
[SFHFKeychainUtils storeUsername:username andPassword:password forServiceName:#"MyService" updateExisting:YES error:nil];
}
}
GETTING DATA
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//verify view is on the login page of the site (simplified)
NSURL *requestURL = [self.webView.request URL];
if ([requestURL.host isEqualToString:#"www.mydomain.com"]) {
//check for stored login credentials
NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:#"username"];
if (username.length != 0 ) {
//create js strings
NSString *loadUsernameJS = [NSString stringWithFormat:#"document.myForm.username.value ='%#'", username];
NSString *password = [SFHFKeychainUtils getPasswordForUsername: username andServiceName:#"MyService" error:nil];
if (password.length == 0 ) password = #"";
NSString *loadPasswordJS = [NSString stringWithFormat:#"document.myForm.password.value ='%#'", password];
//autofill the form
[self.webView stringByEvaluatingJavaScriptFromString: loadUsernameJS];
[self.webView stringByEvaluatingJavaScriptFromString: loadPasswordJS];
}
}
}
Note that I'm using Buzz Andersen's awesome SFHFKeychainUtils package to store sensitive data to the iOs Keychain.
In order to get SFHFKeychainUtils working, you need to do a few things:
Add SFHFKeychainUtils.h and SFHFKeychainUtils.m to your project
Add the Security.framework to your project
#import <Security/Security.h> and #import "SFHFKeychainUtils.h"
From my looking I don't think there is an easy way to do it. Here is an idea of what might work though:
create your uiwebview
create a nsurlrequest
after your webview delegate page loaded function fires look in the request's http body
find the form for login (regex for common login forms?)
retrieve give the user the option to save it and then retrieve later
Related
I have a webview that when loaded, the user is logged in by a POST request. After they are logged in, I want them to be taken to a webpage. My POST request is a URL as follows:
- (void)viewDidLoad {
[super viewDidLoad];
[_scoreWebView setDelegate:self];
NSMutableURLRequest *detailRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myrul"]];
[detailRequest setHTTPMethod:#"POST"];
NSString *sendInfo =[NSString stringWithFormat:#"action=login&EMAIL=email&PASSWORD=password", nil];
NSData *infoData = [sendInfo dataUsingEncoding:NSUTF8StringEncoding];
[detailRequest setHTTPBody:infoData];
[_scoreWebView loadRequest:detailRequest];
}
This log in process works fine. However, after I send the user to my webpage it is launching webviewdidfinishload infinitely. I know that it fires each time something is loaded. Is there an alternate solution to redirecting the user to my page after log in? Also, I have three different pages that the user could be redirected to based on their input, this is just one of them for simplicity. This is my finishload method:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//Check here if still webview is loding the content
if (webView.isLoading)
return;
else //finished
NSLog(#"finished loading");
NSLog(#"%# in calendar", _thisScriptUniqueID);
NSString *fullURL=[NSString stringWithFormat:#"myurl/%##score", _thisScriptUniqueID];
NSLog(#"%#", fullURL);
NSURL *url = [NSURL URLWithString:fullURL];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[_scoreWebView loadRequest:requestObj];
}
Is there a different method that could be used to take the user to the page, or would it be possible to include both in the viewDidLoad?
After a lot of research, I decided that the functionality would work a lot better on the server side. I made it so that the same URL logs the user in and brings them to the desired page at the same time.
I'm developing an app with Login with Facebook and also Login with username and password.
Now i want to find all the events of a public page on Facebook for both the types of users (Facebook and Normal).
The problem is that the User with Facebook can retrieve the data, but the "normal" user cannot because data is nil.
The steps are :
1 - Compile this url with the correct credential of my Facebook App :
https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=APP_SECRET&grant_type=client_credentials
2 - Put the url in my browser and retrieve the App Token In the format :
53682XXXXXXXXXX|w6F3Ic6L48XXXXXXXXXXXXXXXXX
3 - Use this piece of code :
NSString *token = [[[FBSession activeSession] accessTokenData] accessToken];
NSString *urlString;
if (!userWithFb) {
NSString *token = #"53682XXXXXXXXXX|w6F3Ic6L48XXXXXXXXXXXXXXXXX";
urlString = [NSString stringWithFormat:#"https://graph.facebook.com/%#/events?access_token=%#", pageId,token];
}else{
urlString = [NSString stringWithFormat:#"https://graph.facebook.com/%#/events?access_token=%#", pageId,token];
}
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
if(data != nil)
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
BUT the data for the normal user is nil. When I put the "urlstring" in my browser I see all the data , I don't know where is the problem. Waiting for solution I say thanks.
I solved my question by adding this piece of code after the if statement :
NSString *encodedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
url = [NSURL URLWithString:encodedURLString];
NSData* data = [NSData dataWithContentsOfURL:url];
The Problem is the "|" in the token, it must be replaced with "&".
Hope this help you !
I'm an iOS newb (.NET professional), so this may be a simple issue but I couldn't find anything through the SO search or Google (and maybe not looking for the right terms).
I'm writing an app that displays information from a DD-WRT router through it's web interface. I have no problem displaying the initial page and navigating through any of the other pages, but if I make any change on a form (and it redirects to apply.cgi or applyuser.cgi), the UIWebView is blank - it's supposed to display the same page, with the form submission changes. The site works fine in Mobile Safari, which I find intriguing, but I guess UIWebView isn't totally the same.
I think the iOS code is pretty standard for display a webpage, but I'll list it below. I can't give you access to my router because, well, that's not a good idea :) Hopefully someone with a DD-WRT router can help (or know what my issue is anyway).
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *sURL = #"http://user:pass#XXX.XXX.X.X";
NSURL *url = [NSURL URLWithString:sURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
self.webView.delegate = self ;
}
And I'm doing a few things with Javascript in the webViewDidFinishLoad method, but I know that's not the culprit because it still happens when I comment it out.
Well I figured out the problem on my own. I think part of it was putting the username & password in the URL (which was just a temporary measure) because I found that method provided the same results in mobile Safari and desktop Chrome.
So I added MKNetworkKit to my project that provided a simple way to add authentication to my request, and found I had to make a specific request to POST the data, then reloaded the page the to see the changes.
In the (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType method, I check if ([request.HTTPMethod isEqualToString:#"POST"]) and do this:
NSString *sPostData = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
NSArray *aPostData = [sPostData componentsSeparatedByString:#"&"];
NSMutableDictionary *dPostData = [[NSMutableDictionary alloc] init];
//i don't know if this is the best way to set a dictionary, but it works
for (id apd in aPostData)
{
NSString *key = [apd componentsSeparatedByString:#"="][0];
NSString *val = [apd componentsSeparatedByString:#"="][1];
[dPostData setValue:val forKey:key];
}
MKNetworkEngine *engine = [[MKNetworkEngine alloc] init];
MKNetworkOperation *op = [engine operationWithURLString:[request.URL description] params:dPostData httpMethod:#"POST"];
[op setUsername:#"myUserName" password:#"myPassword" basicAuth:YES];
self.postedRequest = TRUE; //a bool I set so, when it comes to webViewDidFinishLoad, I reload the current page
[op start]; //send POST operation
I'm working on a payment system that runs in a UIWebView. The user completes a form on one site, and is taken to a payment gateway to process their card information. Once the payment has been processed, the user is taken back to a confirmation page on the first site.
The sites work as expected when tested in a normal browser, or even Mobile Safari. The sites are black boxes, and I can't change anything inside them. Apparently, the sites use relative URLs, and my issue occurs because my UIWebView is trying to load a page on the second domain with the base URL from the first domain.
For example,
User posts form from http://theform.com/page
User is taken to http://theform.com/OrderCC.aspx?orderRef=e59d7f53a693472cad8a76dd8fb64
In the second step, the user should have been taken to http://thepaymentgateway.com/OrderCC.aspx...
I'm trying to intercept requests to the wrong base URL, and reroute them to a correct one like this:
-(BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest*)inRequest navigationType:(UIWebViewNavigationType)inType {
NSString *wrongURL = #"http://theform.com/OrderCC.aspx";
NSString *request = [[inRequest URL] absoluteString];
NSString *data = [[NSString alloc] initWithData:[inRequest HTTPBody] encoding:NSASCIIStringEncoding];
if ([request length] >= 33 && [[request substringWithRange:NSMakeRange(0, 33)] isEqualToString:wrongURL]) {
[webView loadData:[inRequest HTTPBody] MIMEType:#"text/html" textEncodingName:#"UTF-8" baseURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://thepaymentgateway.com/OrderCC.aspx%#", [request substringFromIndex:33]]]];
return NO;
}
return YES;
}
After this runs, it seems my UIWebView is just displaying a blank page containing the POST data string. Where am I going wrong?
I have a app code that sends in request only when the app opens. However I need to have a continuos access to the file in the server which will be updated every second.
-(void)viewDidLoad
{
[super viewDidLoad];
NSURL *myurl = [NSURL URLWithString:#"URL"];
NSString *mystring = [NSString stringWithContentsOfURL:myurl encoding:NSASCIIStringEncoding error:nil];
//myTextView.text = realtime;
//NSLog(mystring);
NSString *stripped1 = [mystring stringByReplacingOccurrencesOfString:#"\r" withString:#""];
NSArray *rows = [stripped1 componentsSeparatedByString:#"\n"];
}
Is there a way that I can keep checking on my server file to load the data? Currently I can load it only once when I open the application. But its a live update application.
Thank you.
You may use apple notification to notify the user new data.
But I'm not sure if this is what you want.