Restrict UIWebview to certain pages - objective-c

I am trying to create a simple ipad app with a UIWebview that displays a form that a customer can fill in.. what i want to do is restrict the app so that it only allows the user to navigate to certain addresses.. (i.e. either something that allows the user to go to a specific address.. OR something that checks for specific keywords and allows/blocks them as appropriate..)
Could someone please show me how its done..
NB: its basically a googledocs form and i dont want to let the user navigate away from it.. (the user could easily click away and go elsewhere)
Thank you for reading :)

In the class that is your UIWebViewDelegate you can use something like this:
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = request.URL;
NSString *urlString = url.absoluteString;
//Check for your own url. You can use more advanced checking techniques of course :)
NSRange range = [urlString rangeOfString:#"http://www.yourUrl.com"];
if (range.location != NSNotFound)
return YES;
else
return NO;
}

You can use the delegate method
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
to determine whether the UIWebView can load a given web page. Although that would mean knowing exactly which pages are allowed (if there are many this might not be convenient).

Use UIWebViewDelegate method webView:shouldStartLoadWithRequest:navigationType:
Then check what is the URL that the UIWebView tires to load, and act consequently.
UIWebViewDelegate Reference

Related

OS X - WebView questions

I am using webview in one of OS X app and got strange requirement from client that:
1) Need to show address bar in app for webview, I tried if there's any support by webview but couldn't really find. I know I can do that by having a text field and show URL in that but I am not sure if that's the right way so is there any other better way, please suggest.
2) Need to also check whether loaded URL has SSL support and show some icon like padlock (open/close). So again is there any support or feature of Webview that i can use or I just have to check for URL prefix of http or https? Please help.
Thanks in advance.
MP
That is exactly what you should do. Create the text field, and insert this bit of code into your application:
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:#"window.location"];
[myTextField setStringValue:currentURL];
}
For the second part of your question, use the currentURL string, and check if https:// exists in the string using the NSNotFound method. By using [myString rangeOfString:#"string_to_search_for"].location != NSNotFound, it will return true if the rangeOfString: is found. (!= means not equal to.) (So != NSNotFound means that your rangeOfString is not equal to not being found... if that makes sense)
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:#"window.location"];
if ([currentURL rangeOfString:#"https://"].location != NSNotFound) {
// *https://* exists! Show your closed padlock image!
} else {
// *https://* does not exist. Show your open padlock image.
}
}
Hope this was helpful!

How to get back from web view after some action is done there?

I am implementing the CCAvenue gate way in my application.
In which CCAvenue gateway is loaded in web view from my web site.
Now after Transaction is completed with Success/Failur page,How can i came to know and load my apps success page....
Please help me Friends
The easiest way would be for your web page to send a request with a custom scheme for example myapp://transactionSuccess and your app to handle this in the webView delegate:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:#"myapp"]) {
// handle the response
NSString *host = [URL host];
if ([host isEqualToString:#"transactionSuccess"]) {
// show your custom screen
return NO;
}
}
return YES;
}
Dan Spag is correct - URL Schemes make things a lot easier to manage and understand but sometimes these are just not possible. An alternative would be to ensure in the success / failure web page that in the dom (or HTML body response), the state of response is set and then capture that natively in iOS using stringByEvaluatingJavaScriptFromString like this...
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *responseState = [webView stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
// or possibly a javascript property in the webview's loaded page if preferred.
if([responseState isEqualToString:#"success"])
{
// do something with success
}
else if([responseState isEqualToString:#"fail"]) {
// do something with fail
}
[self.webView removeFromSuperView];
}

How do you override the amount of redirects a webView can handle. Obj-C, Cocoa

This is really annoying me. I have a webView, and I am trying to get it too load drive.google.com. However, because of the amount of redirects on that site, the webView does nothing. I know that the code for loading a webView works, and that URL is correct, as I get the didFailWithProvisionalLoadWithError message. That is how I know that it is the redirect problem.
I have looked all over, but can find no way to alter the amount of allowed redirects. I know that it is possible, as Safari can handle it. There has to be some kind of delegate method to override. No code today, as all my searches (4 days worth of them) have lead me to nothing.
If anyone has an idea, let me know. FYI, this is Cocoa (WebView) not cocoa touch (UIWebView). Thanks.
That for which you have been searching is
– webView:decidePolicyForNavigationAction:request:frame:decisionListener:
which is part of the WebPolicyDelegate protocol.
Read the documentation for WebPolicyDelegate, particularly the discussion for this delegate method. The documentation for WebPolicyDecisionListener describes the messages you can send to the listener. You might identify a redirect inside this delegate method, and you could instruct the decision listener to use or ignore it.
EDIT:
I appreciate the 50 points, but if I didn't help you solve your problem, I hardly think I deserve them, and that was enough guilt to motivate me to sit down and try out your problem.
I built a tiny app, one that isn't document based, or uses ARC or garbage collection. I turned off Auto Layout in the MainMenu.xib file, and simply placed an instance of WebView inside it. I modified AppDelegate to include an outlet for the WebView instance.
I then created fleshed out the -applicationDidFinishLaunching: stub like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString *urlString = #"http://drive.google.com";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[[[self webView] mainFrame] loadRequest:urlRequest];
}
and if I'm getting redirects, they don't seem to be affecting my ability to display the page.
Are we on the same page (no pun intended)?
EDIT 2:
Good news (or bad news, depending on your POV): Redirects were never the problem (though they did help to shed light on what was really going on).
When it comes to Google Drive, WebView isn't an approved browser. You can, however, fix that (N.B., below) by modifying the user agent string. Here's the revised code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString *urlString = #"http://drive.google.com";
NSURL *url = [NSURL URLWithString:urlString];
NSString *customUserAgent = [NSString stringWithFormat:#"%# Version/6.0.2 Safari/8536.26.17", [[self webView] userAgentForURL:url]];
[[self webView] setCustomUserAgent:customUserAgent];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[[[self webView] mainFrame] loadRequest:urlRequest];
}
N.B.: The user interface appears to be fully functional, but even so, Google may change its means of determining what the browser is and break this scheme, so consider yourself warned. You might want to explore their SDK for possible alternate means of accessing Google Drive.
As always, good luck to you in your endeavors.
According to the WebView reference class in the Apple documentation, you have to set a delegate that conforms to the webframeloaddelegate protocol.
[webView setFrameLoadDelegate:object];
Then in object, you have to set this method:
- (void)webView:(WebView *)sender willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date forFrame:(WebFrame *)frame

Listening for events in a UIWebView (iOS)

I'm trying to listen for an event (specifically a button click) from a webpage I have opened in a UIWebView in my iOS app. I know that in other languages there are ways to attach event listeners to certain web calls, but I haven't yet found a way to do it in Objective-C. I also haven't found anyone online who says it can't be done, so I decided I should ask. I saw in the documentation that you can make Objective-C calls from Javascript, but I do not have control of the webpage I am wanting to monitor. So I need a solution that allows me to listen for this event entirely in Objective-C.
EDIT: If specifics would help, I am trying to allow the user to make a wallpost on Facebook. I am loading the Facebook sharer page in a UIWebview (http://www.facebook.com/sharer/sharer.php?u=) and wanting to monitor when the user clicks "Share Link" so that I can close the web view.
Thank you all for your time!
You can use the UIWebViewDelegate:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
The UIWebViewNavigationType values are :
enum {
UIWebViewNavigationTypeLinkClicked,
UIWebViewNavigationTypeFormSubmitted,
UIWebViewNavigationTypeBackForward,
UIWebViewNavigationTypeReload,
UIWebViewNavigationTypeFormResubmitted,
UIWebViewNavigationTypeOther
};typedef NSUInteger UIWebViewNavigationType;
Can check this and then look at the NSURLRequest to get info about that
Use a custom scheme. When the user taps the button, ping a url with the custom scheme (myScheme://buttonTapped) and either:
Catch this in the webview delegate method shouldStartLoadWithRequest... (ie check if the URL contains your custom scheme) and route it to the appropriate objective c selector. Or
Register a custom URL protocol and set it up to handle your custom URL scheme. Something like the below:
#implementation MyURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
return [[[request URL] scheme] isEqualToString:#"myScheme"];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)startLoading
{
NSURLRequest * request = [self request];
id client = [self client];
NSData * data = [NSMutableData dataWithCapacity:0];
NSHTTPURLResponse * response = [[[NSHTTPURLResponse alloc] initWithURL:[request URL] statusCode:200 HTTPVersion:#"HTTP/1.1" headerFields:nil] autorelease];
[client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[client URLProtocol:self didLoadData:data];
[client URLProtocolDidFinishLoading:self];
[[NSNotificationCenter defaultCenter] postNotificationName:kSchemeNotification object:nil userInfo:payload];
}
- (void)stopLoading
{
}

Cocoa/WebKit, having "window.open()" JavaScript links opening in an instance of Safari

I am building a really basic Cocoa application using WebKit, to display a Flash/Silverlight application within it. Very basic, no intentions for it to be a browser itself.
So far I have been able to get it to open basic html links (<a href="..." />) in a new instance of Safari using
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
Now my difficulty is opening a link in a new instance of Safari when window.open() is used in JavaScript. I "think" (and by this, I have been hacking away at the code and am unsure if i actually did or not) I got this kind of working by setting the WebView's policyDelegate and implementing its
-webView:decidePolicyForNavigationAction:request:frame:decisionListener:
delegate method. However this led to some erratic behavior.
So the simple question, what do I need to do so that when window.open() is called, the link is opened in a new instance of Safari.
Thanks
Big point, I am normally a .NET developer, and have only been working with Cocoa/WebKit for a few days.
I made from progress last night and pinned down part of my problem.
I am already using webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: and I have gotten it to work with anchor tags, however the method never seems to get called when JavaScript is invoked.
However when window.open() is called webView:createWebViewWithRequest:request is called, I have tried to force the window to open in Safari here, however request is always null. So I can never read the URL out.
I have done some searching around, and this seems to be a known "misfeature" however I have not been able to find a way to work around it.
From what I understand createWebViewWithRequest gives you the ability to create the new webview, the the requested url is then sent to the new webView to be loaded. This is the best explanation I have been able to find so far.
So while many people have pointed out this problem, I have yet to see any solution which fits my needs. I will try to delve a little deeper into the decidePolicyForNewWindowAction again.
Thanks!
Well, I'm handling it by creating a dummy webView, setting it's frameLoad delegate to a custom class that handles
- (void)webView:decidePolicyForNavigationAction:actionInformation :request:frame:decisionListener:
and opens a new window there.
code :
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
//this is a hack because request URL is null here due to a bug in webkit
return [newWindowHandler webView];
}
and NewWindowHandler :
#implementation NewWindowHandler
-(NewWindowHandler*)initWithWebView:(WebView*)newWebView {
webView = newWebView;
[webView setUIDelegate:self];
[webView setPolicyDelegate:self];
[webView setResourceLoadDelegate:self];
return self;
}
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
}
-(WebView*)webView {
return webView;
}
There seems to be a bug with webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: in that the request is always nil, but there is a robust solution that works with both normal target="_blank" links as well as javascript ones.
Basically I use another ephemeral WebView to handle the new page load in. Similar to Yoni Shalom but with a little more syntactic sugar.
To use it first set a delegate object for your WebView, in this case I'm setting myself as the delegate:
webView.UIDelegate = self;
Then just implement the webView:createWebViewWithRequest: delegate method and use my block based API to do something when a new page is loaded, in this case I'm opening the page in an external browser:
-(WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
return [GBWebViewExternalLinkHandler riggedWebViewWithLoadHandler:^(NSURL *url) {
[[NSWorkspace sharedWorkspace] openURL:url];
}];
}
That's pretty much it. Here's the code for my class. Header:
// GBWebViewExternalLinkHandler.h
// TabApp2
//
// Created by Luka Mirosevic on 13/03/2013.
// Copyright (c) 2013 Goonbee. All rights reserved.
//
#import <Foundation/Foundation.h>
#class WebView;
typedef void(^NewWindowCallback)(NSURL *url);
#interface GBWebViewExternalLinkHandler : NSObject
+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler;
#end
Implemetation:
// GBWebViewExternalLinkHandler.m
// TabApp2
//
// Created by Luka Mirosevic on 13/03/2013.
// Copyright (c) 2013 Goonbee. All rights reserved.
//
#import "GBWebViewExternalLinkHandler.h"
#import <WebKit/WebKit.h>
#interface GBWebViewExternalLinkHandler ()
#property (strong, nonatomic) WebView *attachedWebView;
#property (strong, nonatomic) GBWebViewExternalLinkHandler *retainedSelf;
#property (copy, nonatomic) NewWindowCallback handler;
#end
#implementation GBWebViewExternalLinkHandler
-(id)init {
if (self = [super init]) {
//create a new webview with self as the policyDelegate, and keep a ref to it
self.attachedWebView = [WebView new];
self.attachedWebView.policyDelegate = self;
}
return self;
}
-(void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
//execute handler
if (self.handler) {
self.handler(actionInformation[WebActionOriginalURLKey]);
}
//our job is done so safe to unretain yourself
self.retainedSelf = nil;
}
+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler {
//create a new handler
GBWebViewExternalLinkHandler *newWindowHandler = [GBWebViewExternalLinkHandler new];
//store the block
newWindowHandler.handler = handler;
//retain yourself so that we persist until the webView:decidePolicyForNavigationAction:request:frame:decisionListener: method has been called
newWindowHandler.retainedSelf = newWindowHandler;
//return the attached webview
return newWindowHandler.attachedWebView;
}
#end
Licensed as Apache 2.
You don't mention what kind of erratic behaviour you are seeing. A quick possibility, is that when implementing the delegate method you forgot to tell the webview you are ignoring the click by calling the ignore method of the WebPolicyDecisionListener that was passed to your delegate, which may have put things into a weird state.
If that is not the issue, then how much control do you have over the content you are displaying? The policy delegate gives you easy mechanisms to filter all resource loads (as you have discovered), and all new window opens via webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:. All window.open calls should funnel through that, as will anything else that triggers a new window.
If there are other window opens you want to keep inside your app, you will to do a little more work. One of the arguments passed into the delegate is a dictionary containing information about the event. Insie that dictionary the WebActionElementKey will have a dictionary containing a number of details, including the original dom content of the link. If you want to poke around in there you can grab the actual DOM element, and check the text of the href to see if it starts with window.open. That is a bit heavy weight, but if you want fine grained control it will give it to you.
By reading all posts, i have come up with my simple solution, all funcs are in same class,here it is, opens a link with browser.
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
return [self externalWebView:sender];
}
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
{
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
}
-(WebView*)externalWebView:(WebView*)newWebView
{
WebView *webView = newWebView;
[webView setUIDelegate:self];
[webView setPolicyDelegate:self];
[webView setResourceLoadDelegate:self];
return webView;
}
Explanation:
Windows created from JavaScript via window.open go through createWebViewWithRequest.
All window.open calls result in a createWebViewWithRequest: with a null request, then later a location change on that WebView.
For further information, see this old post on the WebKit mailing list.
An alternative to returning a new WebView and waiting for its loadRequest: method to be called, I ended up overwriting the window.open function in the WebView's JSContext:
First, I set my controller to be the WebFrameLoadDelegate of the WebView:
myWebView.frameLoadDelegate = self;
Then, in the delegate method, I overwrote the window.open function, and I can process the URL there instead.
- (void)webView:(WebView *)webView didCreateJavaScriptContext:(JSContext *)context forFrame:(WebFrame *)frame{
context[#"window"][#"open"] = ^(id url){
NSLog(#"url to load: %#", url);
};
}
This let me handle the request however I needed to without the awkward need to create additional WebViews.