guys. I'm trying to realize opening link from my webView in safari. Sometimes it works perfect, sometimes showing exc bad access.
This is my code:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
urlWillShow = [request mainDocumentURL];
return YES;
}
- (IBAction)openInSafari {
NSLog(#"Will try to open in safari link: %#", [urlWillShow absoluteString]);
if ([[urlWillShow scheme] isEqualToString:#"http"] || [[urlWillShow scheme] isEqualToString:#"https"]) {
BOOL canLoad = [[UIApplication sharedApplication] canOpenURL:urlWillShow];
if (canLoad == YES) {
[[UIApplication sharedApplication] openURL:urlWillShow];
}
}
}
Use [request URL] instead of [request mainDocumentURL]
Also, you need to retain (if not using ARC) urlWillShow as its likely being released before your action is called.
Alternatively, make the UIWebView an IBOutlet and instead of storing urlWillShow in an ivar, you can use [[self.webView request] URL] in your action method.
Related
I am receiving a description text in HTML format, and I am loading it in a webview, if a link clicked in the description so I load it in separate view controller. But shouldStartLoadWithRequest giving a some appended link. here is my code
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if(navigationType == UIWebViewNavigationTypeLinkClicked) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
WebsiteViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"WebsiteViewController"];
vc.url = request.URL.absoluteString;
NSLog(#"link is : %#", [[request URL] absoluteString]);
[self.navigationController pushViewController:vc animated:YES];
return false;
}
return true;
}
it prints this
link is : applewebdata://038EEEBF-A4C9-4C7D-8FB5-32056714B855/www.yahoo.com
and I am loading it like this
[webViewDescription loadHTMLString:description baseURL:nil];
As you are using loadHTMLString and you are setting baseURL to nil therefore applewebdata URI scheme is used by iOS instead of the “http” in URIs used for accessing internal resources on the device. You could try setting the baseURL
I had a similar issue. In practice, setting the baseURL to 'http://' or something like that wasn't working for me either. I also only saw the applewebdata scheme about 50% of the time, the other 50% of the time I saw the correct scheme I was expecting.
To resolve this, I ended up intercepting -webView:shouldStartLoadWithRequest:navigationType: callbacks and using a regular expression to strip out Apple's applewebdata scheme. Here's what it ended up looking like
// Scheme used to intercept UIWebView callbacks
static NSString *bridgeScheme = #"myCoolScheme";
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
BOOL shouldStartLoad = YES;
NSURL *requestURL = request.URL;
// Strip out applewebdata://<UUID> prefix applied when HTML is loaded locally
if ([requestURL.scheme isEqualToString:#"applewebdata"]) {
NSString *requestURLString = requestURL.absoluteString;
NSString *trimmedRequestURLString = [requestURLString stringByReplacingOccurrencesOfString:#"^(?:applewebdata://[0-9A-Z-]*/?)" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, requestURLString.length)];
if (trimmedRequestURLString.length > 0) {
requestURL = [NSURL URLWithString:trimmedRequestURLString];
}
}
if ([requestURL.scheme isEqualToString:bridgeScheme]) {
// Do your thing
shouldStartLoad = NO;
}
return shouldStartLoad;
}
I am teaching myself how to program an iPhone app by looking at code examples from various sources online, so it is fair to say that I do not understand the language (yet).
I am have successfully built a UIWebView browser app that goes to a login page. However, I am trying to take it one step further by having the
Following Byron's code on his own stack overflow question, I have tried to follow in his footsteps.
Is it possible for a UIWebView to save and autofill previously entered form values (e.g., username & password)?
However, when the following line of code is live in the app, the browser will only load a blank page.
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; {
I really appreciate any assistance to help get me back on the right track. Thank you very much,
Below is my entire code:
#import "ViewController.h"
// 6/13/2012 added to cache username and password
#import
#import "SFHFKeychainUtils.h"
// -----
#interface ViewController ()
#end
#implementation ViewController
#synthesize webView;
#synthesize spinner;
-(IBAction)goBack:(id)sender{
if ([webView canGoBack]) {
[webView goBack];
}
}
-(IBAction)goForward:(id)sender{
if ([webView canGoForward]){
[webView goForward];
}
}
- (void)viewDidLoad
{
NSURL *url = [NSURL URLWithString:#"http://xxxxxx.com/weblink/hh_login.html"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
// 6/13/2012 added to cache username and password
- (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];
}
}
// -----
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[spinner startAnimating];
}
//- (void)webViewDidFinishLoad:(UIWebView *)webView {
// [spinner stopAnimating];
//}
// 6/13/2012 added to cache username and password
- (void)webViewDidFinishLoad:(UIWebView *)webView{
[spinner stopAnimating];
//verify view is on the login page of the site (simplified)
NSURL *requestURL = [self.webView.request URL];
if ([requestURL.host isEqualToString:#"http://xxxxxx.com/weblink/hh_login.html"]) {
//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];
}
}
}
// -----
#end
TRY BELOW ONE
You need to fill the credential when webview has loaded .
below is the delegate method of UIWebView called when the WebView has loaded.At this time pass the credential not as were passing before loading the UIWebView which is not correct.
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if(!isLoggedIn){//just load only onetime.
//pass the login Credintails into textfield of WebView.
NSString* userId = #"userName" //here just replace that string to the username
NSString* password = #"password";//here just replace that string to the password
if(userId != nil && password != nil ){
NSString* jScriptString1 = [NSString stringWithFormat:#"document.getElementById('username').value='%#'", userId];
//username is the id for username field in Login form
NSString* jScriptString2 = [NSString stringWithFormat:#"document.getElementById('password').value='%#'", password];
//here password is the id for password field in Login Form
//Now Call The Javascript for entring these Credential in login Form
[webView stringByEvaluatingJavaScriptFromString:jScriptString1];
[webView stringByEvaluatingJavaScriptFromString:jScriptString2];
//Further if you want to submit login Form Automatically the you may use below line
[webView stringByEvaluatingJavaScriptFromString:#"document.forms['login_form'].submit();"];
// here 'login_form' is the id name of LoginForm
}
isLoggedIn=TRUE;
}
}
It'll really help you.
I have an app in the process of upgrading to the iOS5 SDK and Phonegap 1.0.0
The Childbrowser plugin is working properly, but when a link to the iTunes app store is clicked - the link is opened in a Childbrowser window.
I would prefer it to open in Appstore directly, which is what happens if I do not use the ChildBrowser plugin.
This is the appstore link (points to a submit a review page within the appstore)
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=386470812&pageNumber=0&sortOrdering=1&type=Purple+Software&mt=8
and this is how AppDelegate is modified
AppDelegate.m, scroll way down and replace the following:
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return [ super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType ];
}
with this:
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:
(NSURLRequest *)request navigationType:
(UIWebViewNavigationType)navigationType
{
NSURL *url = [request URL];
if ([[url scheme] isEqualToString:#"gap"] || [url isFileURL]) {
return [ super webView:theWebView shouldStartLoadWithRequest:request
navigationType:navigationType ];
}
else {
ChildBrowserViewController* childBrowser =
[ [ ChildBrowserViewController alloc ] initWithScale:FALSE ];
[super.viewController presentModalViewController:childBrowser
animated:YES ];
[childBrowser loadURL:[url description]];
[childBrowser release];
return NO;
}
}
I used the method outlined in this blogpost to get the Childbrowser up and running
http://iphonedevlog.wordpress.com/2011/09/24/installing-childbrowser-into-xcode-4-with-phonegap-1-0-mac-os-x-snow-leopard/
Any thoughts on how to change this to produce the desired action?
Many thanks..
You need to check whether your URL contain http://itunes.apple.com/ as a substring by using rangeOfString: method and location attribute.
Please confirm your javascript called the url like this, window.location="http://itunes.apple.com/us/app/code-check-basic-free-medical/id386470812?mt=8"; or you can use any jquery method.
Please replace shouldStartLoadWithRequest: method with the following snippet.
/**
* Start Loading Request
* This is where most of the magic happens... We take the request(s) and process the response.
* From here we can re direct links and other protocalls to different internal methods.
*/
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:
(NSURLRequest *)request navigationType:
(UIWebViewNavigationType)navigationType
{
NSURL *url = [request URL];
if ([[url scheme] isEqualToString:#"gap"] || [url isFileURL]) {
return [ super webView:theWebView shouldStartLoadWithRequest:request
navigationType:navigationType ];
}
else {
//here we will check whether urlString has http://itunes.apple.com/ as substring or not
NSString* urlString=[url absoluteString];
if ([urlString rangeOfString:#"http://itunes.apple.com/"].location == NSNotFound){
ChildBrowserViewController* childBrowser = [ [ ChildBrowserViewController alloc ] initWithScale:FALSE ];
childBrowser.modalPresentationStyle = UIModalPresentationFormSheet;
childBrowser.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[super.viewController presentModalViewController:childBrowser animated:YES ];
[childBrowser loadURL:urlString];
[childBrowser release];
return NO;
}
else
return YES;
}
}
thanks,
Mayur
I want to make an app that uses on the Google Reader API. But I'm finding out that there isn't an offical API for it - is there a problem using the unofficial API, in terms of App Store guidelines/approval? Would other apps (Reeder, etc) use this?
Also what is the best method for logging in? Is OAuth the preffered method? Is using Janrain a good idea?
Frankly Apple doesn't care if you use Google's unofficial API.
I worked for a customer on a RSS reader app that used Google Reader for syncing. We didn't use OAuth but the standard HTTP login which returns you a cookie where you'll have to extract a token from to use in consecutive calls to the various reader URLs.
I can post you the login code from my (old) proof of concept app.
It uses ASIHTTP and some custom string categories. The idea is to send a login request, get the response and extract the session ID/auth code from the response's cookie header. Then you can use that session ID/auth code for consecutive calls.
#pragma mark -
#pragma mark login
//this is your sessionID token you get from the login
//use this in consecutive calls to google reader
//this method returns you the header string you have to add to your request
//[request addRequestHeader: #"Cookie" value: [self sidHeader]];
- (NSString *) sidHeader
{
return [NSString stringWithFormat: #"SID=%#", [self sid]];
}
- (NSString *) authHeader
{
return [NSString stringWithFormat: #"GoogleLogin auth=%#",[self auth]];
}
//login to your google account and get the session ID
- (void) login
{
NSString *username = #"my.googlelogin#gmail.com";
NSString *password = #"mypassword123";
NSString *loginUrl = #"https://www.google.com/accounts/ClientLogin?client=NNW-Mac";
NSString *source = #"NNW-Mac"; //let's fake NetNewsWire
NSString *continueUrl = #"http://www.google.com";
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString: loginUrl]]; // log in & get cookies
[request addRequestHeader: #"User-Agent" value: #"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];
[request setPostValue: username forKey: #"Email"];
[request setPostValue: password forKey: #"Passwd"];
[request setPostValue: #"reader" forKey: #"service"];
[request setPostValue: source forKey: #"source"];
[request setPostValue: continueUrl forKey: #"continue"];
[request setDelegate: self];
[request setDidFailSelector: #selector(loginRequestFailed:)];
[request setDidFinishSelector: #selector(loginRequestFinished:)];
[request start];
}
-(void)loginRequestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
//login failed
if ([responseString containsString: #"Error=BadAuthentication" ignoringCase: YES])
{
[self setLastError: [self errorWithDescription: #"Bad Username/Passsword" code: 0x001 andErrorLevel: 0x00]];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
return NO;
}
//captcha required
if ([responseString containsString: #"CaptchaRequired" ignoringCase: YES])
{
[self setLastError: [self errorWithDescription: #"Captcha Required" code: 0x001 andErrorLevel: 0x00]];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
return NO;
}
//extract SID + auth
NSArray *respArray = [responseString componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
NSString *sidString = [respArray objectAtIndex: 0];
sidString = [sidString stringByReplacingOccurrencesOfString: #"SID=" withString: #""];
[self setSid: sidString];
NSString *authString = [respArray objectAtIndex: 2];
authString = [authString stringByReplacingOccurrencesOfString: #"Auth=" withString: #""];
[self setAuth: authString];
//mesage delegate of success
if ([delegate respondsToSelector: #selector(gReaderLoginDidSucceed:)])
{
[delegate gReaderLoginDidSucceed: self];
}
return YES;
}
- (void)loginRequestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
//NSLog(#"login request failed with error: %#", [error localizedDescription]);
[self setLastError: error];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
}
After login you can use sid and auth to forge requests to the Reader's API endpoints.
Example:
- (ASIHTTPRequest *) requestForAPIEndpoint: (NSString *) apiEndpoint
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString: apiEndpoint]];
[request addRequestHeader: #"User-Agent" value: #"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];
[request addRequestHeader: #"Cookie" value: [self sidHeader]];
[request addRequestHeader: #"Authorization" value: [self authHeader]];
return request;
}
An interesting read about Google Reader and its private API is http://timbroder.com/2007/08/google-reader-api-functions.html
Please make sure to read the latest comments :)
/edit: I updated the code to use the auth header (which google introduced in june this year). I guess this would be the place to put your OAuth token in if you would use OAuth. guess
Ive since found this: "The Google Data APIs Objective-C Client Library provides an iPhone static library, a Mac OS X framework, and source code that make it easy to access data through Google Data APIs. " code.google.com/p/gdata-objectivec-client - which is great!
It doesn't include the Reader API however (because it's not been released).
I have been able to access the API by changing (in the OAuthSampleTouch example)
NSString *scope = #"http://www.google.com/m8/feeds/";
in OAuthSampleRootViewControllerTouch.m to
NSString *scope = #"http://www.google.com/reader/api/*";
and
urlStr = #"http://www.google.com/m8/feeds/contacts/default/thin";
to
urlStr = #"http://www.google.com/reader/atom/user/-/label/Design";
where Design is a folder name - check this http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI its a great help.
Update
I have since found that this technique to be the best / lightest / less-complicated :
Native Google Reader iPhone Application
Why doesn't this code work? It shows the Google screen but it doesn't change the text box value. I confirmed that the JS does work by running it in Safari, and this code seems to work otherwise since running alert('hi') does work.
NSURL *web_url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *web_request = [NSURLRequest requestWithURL:web_url];
[web_screen loadRequest:web_request];
NSString *js_result = [web_screen stringByEvaluatingJavaScriptFromString:#"document.getElementsByTagName('input')[1].value='test';"];
Just expanding on the previous answer. You need to conform to the UIWebViewDelegate protocol by setting the delegate property of the UIWebView like this:
web_screen.delegate = self;
Then you can implement one of the delegate methods to know when a request has finished loading and is therefore ready to have scripts run like so:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *js_result = [webView stringByEvaluatingJavaScriptFromString:#"document.getElementsByTagName('input')[1].value='test';"];
}
For more information on the UIWebViewDelegate protocol visit the Apple site http://developer.apple.com/library/ios/#documentation/uikit/reference/UIWebViewDelegate_Protocol/Reference/Reference.html
The Load URL action takes place asynchronously. It does not even start until your method has ended. So your javascript is being pushed into an empty UIWebView, then your method ends, then the load happens.
You need to let your method end before the js is pushed in. The standard approach for this is to use a Delegate object, which will have a method called on it when the load completes. Only then does it make sense to push in the javascript.
Does it work if you wait for the page to finish loading first?
Consider looking at NSURLConnection and its delegate methods. You can use these to check on the status of a download.
#interface
...
NSURLConnection *connectionInProgress;
NSData *googleRequestResponseData;
NSURL *googleURL;
...
#implementation
...
- (void) setUpRequest {
googleURL = [[NSURL URLWithString:#"http://www.google.com/"] retain];
googleRequestResponseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30.0];
connectionInProgress = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
#pragma mark NSURLConnection delegate methods
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[googleRequestResponseData appendData:data];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[web_screen loadData:googleRequestResponseData MIMEType:#"application/octet-stream" textEncodingName:#"utf-8" baseURL:googleURL];
NSString *js_result = [web_screen stringByEvaluatingJavaScriptFromString:#"document.getElementsByTagName('input')[1].value='test';"];
NSLog (js_result);
[googleURL release];
[googleRequestResponseData release];
[connectionInProgress release];
connectionInProgress = nil;
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog (#"Connection failed to load data from Google!");
[googleURL release];
[googleRequestResponseData release];
[connectionInProgress release];
connectionInProgress = nil;
}
Alternatively, check out Ben Copsey's ASIHTTPRequest wrapper, which includes a simplified approach to asynchronous downloads (see ASINetworkQueue, specifically).
You can use ASINetworkQueue to run a method of your choice (to run the Javascript code, for example), once the request download is complete.
add '.innerHTML' after what you are searching for
In your case do the following
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *js_result = [webView stringByEvaluatingJavaScriptFromString:#"document.getElementsByTagName('input')[1].value='test'.innerHTML"];
}
This worked for me .
check this for more info here