iOS UIWebView, how to block a certain URL pattern from loading? - javascript

I have an iOS app with a web view controller, which is a delegate of a web view. I would like my UIWebView to never show a url which has a certain keyword or part of a URL. (In my case, I want to block a misleading error page from the app server, which can appear in response to any request).
I'm listening to these events:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {}
-(void)webViewDidFinishLoad:(UIWebView *)webView{}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {}
I'm not sure if listening to these events is enough to make sure that all internal page redirects, including those of javascript would be inspected and the offending url will be prevented from loading.
How can I create a UIWebView which will never show a certain page?

You can check if the keyword is in the URL that was requested and stop the page from loading by returning false. I am assuming the keyword you are looking for will be in the URL? Let's pretend it's errorPage.
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *requestedURL = request.URL
NSString *stringCheck = requestedURL.absoluteString;
// This is the important part--check if errorPage is in the URL.
NSRange range = [urlString rangeOfString:#"errorPage"];
if (range.location == NSNotFound)
return YES;
else
return NO;
}

Related

How to get the show text on current screen in iOS webview

I want to add bookmark on the iOS UIWebView.
webview is loaded html document.
bookmark data should be the text content position on current displaying screen in webview.
If it is possible , please help me.
Thanks.
With the UIWebView you get delegate methods:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;
So you gonna check the URL from webViewDidFinishLoad like so:
- (void) webViewDidFinishLoad:(UIWebView *)webView
{
NSString *requestUrlString = webView.request.URL.absoluteString;
NSLog(#"requestUrlString : %#", requestUrlString);
}
Then you can open and save the URL as a bookmark with CoreData or NSUserDefaults like this:
// New bookmark
NSDictionary *bookmark = #{#"bookmark_title":#"My Bookmark", #"bookmark_url":#"https://www.google.com/"};
// Get bookmarks
NSMutableArray *bookmarks = [[NSUserDefaults standardUserDefaults] objectForKey:#"myBookmarks"];
// Update bookmarks
[bookmarks addObject: bookmark];
[[NSUserDefaults standardUserDefaults] setObject:bookmarks forKey:#"myBookmarks"]
[[NSUserDefaults standardUserDefaults] synchronize];

EasyJSWebView breaks when reloading the webview

I have been implementing EasyJSWebView into a project that pops up a web view on the receipt of a push message. This works fine for the most part, until you want to reload the current webview. At that point the functionality of EasyJSWebView breaks and the javascript in the webpage cannot communicate with the objective C running in the app.
This is the code I'm using to add the interface to the webview and then load the page on the view.
_webView.delegate = self;
javaScriptInterface* interface = [javaScriptInterface new];
[self.webView addJavascriptInterfaces:interface WithName:#"interface"];
interface.webView = self;
[interface release];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"mywebpage.com"]]];
If I call another loadrequest on the webview, even if it is to the same page, this stops working. I have tried adding another interface object to the webview before a reload but that doesn't seem to do anything either.
At the moment I am destroying the current webview and creating a new one each time I need to do a reload or another push message is received.
Is there any fix or workaround to this? I would like to be able to use one webview for everything and not worry if it gets reloaded or not.
There's a bug in the original EasyJSWebView repository.
Just use the fork from here: https://github.com/andiradulescu/EasyJSWebView
I'm not sure if this is the best way to solute the issue, but it works in my project. My method is to create an new UIWebView before every reload or redirect request. Below is the details:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
//'isNotFirstLoad' marks if it is the fisrt load.
if (isNotFirstLoad) {
//create an new webview
CGRect frame = _myWebView.frame;
[_myWebView removeFromSuperview];
_myWebView = [[EasyJSWebView alloc] initWithFrame:frame];
[self.view addSubview:_myWebView];
//init the new webview
_webView.delegate = self;
javaScriptInterface* interface = [javaScriptInterface new];
[self.webView addJavascriptInterfaces:interface WithName:#"interface"];
[_myWebView loadRequest:request];
//reset the firstload flag to load the new request
isNotFirstLoad = NO;
return NO;
}
isNotFirstLoad = YES;
return YES;
}

Run JS in iOS webView every time loading a new page

For example, I want to run this part of code every time user click a new link in my UIWebView,
NSString* js =
#"var meta = document.createElement('meta'); "
"meta.setAttribute( 'name', 'viewport' ); "
"meta.setAttribute( 'content', 'width = device-width;initial-scale=1.0; maximum-scale=1.0' ); "
"document.getElementsByTagName('head')[0].appendChild(meta)";
[webView stringByEvaluatingJavaScriptFromString: js];
how can I do that? which API should I look at?
And I want this JS run in new page. How should I determine if the new page is fully loaded? –
There are two things likely to go wrong in this scenario: 1) your Js throws an exception and 2) the page reloads after your Js has run, and therefore reset the effects of your script.
In my experience, when a Javascript doesn't seem to run in a UIWebView, it's almost always the Javascript that is throwing an exception.
Try surrounding the script in try { ... } catch { ... } to capture the exception:
NSString* js =
#"try {"
"var meta = document.createElement('meta'); "
// [rest of the script...]
"document.getElementsByTagName('head')[0].appendChild(meta)";
" } catch (exc) { "
" window.ERR = exc.toString(); "
" } "
Now you can set a breakpoint in Xcode after your call to stringByEvaluatingJavaScriptFromString:. An error message might be available if you run
po [webView stringByEvaluatingJavaScriptFromString:#"window.ERR"]
In your debug prompt.
A good idea when you try out Javascript on your UIWebView is to first do exploratory
work in something like the Chrome Developer Tools to weed out the worst errors (e.g. syntax errors). If your code works there, the next step is to connect to the view using
Safaris Web inspector for iOS and see if it still does what it should.
#nilveryboring's point that you add scripts in webViewDidFinishLoading rather than in shouldStartLoadWithRequest is correct. In shouldStartLoadWithRequest the page hasn't been loaded yet, and any state you introduce at that point will be discarded by the loading page.
You can detect when the user clicks a link with:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
Once you have detected that he has clicked on a link, perform your js.
More on it in the Docs.

Phonegap (Cordova 1.9.0 based) alerts are not working when the shouldStartLoadWithRequest method is enabled

Phonegap notifications (navigator.notification.alert and navigator.notification.confirm) are not working while using shouldStartLoadWithRequest for URL request processing.
Javascript:
function onBodyLoad() {
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady() {
navigator.notification.alert("Cordova is working");
}
Objective C:
- (BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType{
if ([[[request URL] absoluteString] hasPrefix:#"js-call:"]) {
// Extract the selector name from the URL
NSString *requestString = [[request URL] absoluteString];
NSArray *components = [requestString componentsSeparatedByString:#":"];
NSString *function = [components objectAtIndex:1];
NSLog(#"the function name is %#",function);
// Call the given selector
[self performSelector:NSSelectorFromString(function)];
// Cancel the location change
return NO;
}
return YES;
}
Please help me out.
Ran into very similar issue. Changing the last line
return YES;
to
return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
All commands to plugins go through gap:// bridge. shouldStartLoadWithRequest gets called for every such call. So, defaulting to YES appears to be the issue. When in fact, it should return NO for gap:// calls.

Can I handle alert inside UIWebViewDelegate?

<script language="javascript">
alert("Hell! UIWebView!");
</script>
I can see the alert message inside my UIWebView but can I handle this situation?
Update:
I'm loading a web-page into my UIWebView:
- (void)login {
NSString *requestText = [[NSString alloc] initWithFormat: #"%#?user=%#&password=%#", DEFAULT_URL, user.name, user.password]; // YES, I'm using GET request to send password :)
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:requestText]];
[webView loadRequest:request];
}
The target page contain a JS. If user name or password is incorrect this JS show alert.
I have not any access to its sources.
I want to handle it inside my UIWebViewDelegate.
A better solution to this problem is to create a Category for UIWebView for the method
webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:
So that you can handle the alert event in any way that you'd like. I did this because I don't like the default behavior of UIWebView when it puts the filename of the source in the UIAlertView title. The Category looks something like this,
#interface UIWebView (JavaScriptAlert)
- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame;
#end
#implementation UIWebView (JavaScriptAlert)
- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
UIAlertView* dialogue = [[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil];
[dialogue show];
[dialogue autorelease];
}
#end
This seems to do it:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
JSContext *ctx = [webView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
ctx[#"window"][#"alert"] = ^(JSValue *message) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"JavaScript Alert" message:[message toString] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
};
}
Note: only tested on iOS 8.
If by "contain a flash" you mean the page you're loading into your web view has an Adobe Flash movie in it, you're out of luck, I'm afraid. Mobile Safari doesn't support Flash, and most likely never will.
In the general case, if you want JavaScript running in a web view to communicate with the native app hosting it, you can load fake URLs (for example: "myapp://alert?The+text+of+the+alert+goes+here."). That will trigger the webView:shouldStartLoadWithRequest:navigationType: delegate method. In that method, inspect the request, and if the URL being loaded is one of these internal communications, trigger the appropriate action in your app, and return NO.

Categories