I’ve been working on a PhoneGap project with iPad as the target. Most of the app runs from an external site so after presenting local files to handle the login form and authentication the main webview then shifts to an external site where the rest of the code runs.

We are using ChildBrowser to handle showing images and PDF files from the external site so that we can don’t have to include a navigation bar in our app. The ChildBrowser plugin helps us view those files without getting locked out of the original app. That’s just the background. Now we have a new problem. Many pages in this app link to external sites that are not part of the main application. We needed ChildBrowser to handle all non-app site requests automatically so that external sites would open in the popup and not replace the view in the main UIWebView of PhoneGap. Here is how we solved it:

We started with the PhoneGap.plist preferences file. In the ExternalHosts array within that plist we added all the possible host combinations that our app considers as local to the app. This app has about seven url host variants that could be used within the app so all of those should open in the main WebView. Any host not listed in ExternalHosts should open in the ChildBrowser. Keep in mind that to allow all external sites to open we still had to put * as an entry in ExternalHosts but this doesn’t affect the logic below.

In AppDelegate.m didFinishLaunchingWithOptions method I added this logic to load the PhoneGap.plist into a Dictionary and read the ExternalHosts into an array. If anyone can tell me this is already loaded somewhere and I could have skipped this step let me know in the comments:

NSDictionary *phoneGapPrefs = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PhoneGap" ofType:@"plist"]];
self.internalUrls = [[NSArray alloc] initWithArray:[phoneGapPrefs objectForKey:@"ExternalHosts"]];

I’m assuming you know how to create the internalUrls variable as an instance variable in AppDelegate.h. Now I have an array of all my internal host names so that any requests that don’t match that are loaded in ChildBrowser. Now we proceed down to the shouldStartLoadWithRequest method where I add this logic to check if we’re loading an external site (starts with http) and check if that site is part of our internalUrls array.


if ([[url scheme] isEqualToString:@"http"] || [[url scheme] isEqualToString:@"https"]) {
    //This code will open any url that is not in the PhoneGap whitelist in Child Browser
    if (![m3Urls containsObject:[url host]])
    {
        [theWebView sizeToFit];
        ChildBrowserViewController* childBrowser = [ [ ChildBrowserViewController alloc ] initWithScale:FALSE ];
        childBrowser.modalPresentationStyle = UIModalPresentationFormSheet;
        childBrowser.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;   
        [super.viewController presentModalViewController:childBrowser animated:YES ];   
        NSString* urlString=[NSString stringWithFormat:@"%@",[url absoluteString]]; 
        [childBrowser loadURL:urlString];
        [childBrowser release];
        return NO;      
    }
    return YES;
}

Now all urls that start with http or https as the scheme and are not in the ExternalHosts I’ve listed are considered external to our app and are loaded in ChildBrowser. At this point you might tell me that since I’m listing * as a valid ExternalHost I don’t need to list all the site-specific urls and you’d be correct as far as avoiding the whitelist. During initial development I didn’t even anticipate opening non-site urls so all I wanted to list were the site-specific urls for this app. Later on I added the * for allowing any url, but realize that listing all my internal app urls provided an easy way to accomplish my need to determine which urls were for my app and which were references to non-app sites.