Something cool that many mobile developers (and particularly native developers) might not be aware of at this point, is the option to use PhoneGap (aka Cordova) as a component within your mobile application, as a web “subview” of a whole native application where you want to render HTML/JS content and/or interact with the PhoneGap APIs. In this webview component (sometimes also referred to as Cleaver), you can do anything a traditional PhoneGap application would do, such as access native features like the camera or contacts etc too. This post will expand the steps here and show you a sample application you could download and try yourself. Note that I am not an Objective-C native programmer, but this is just a simple example to show off the capabilities and create awareness. Feel free to post your application or source if you have used this and would like to share with others .
Below are detailed steps on including PhoneGap/Cordova as a web subview in your native application. At the end of this section I show screenshots from a sample application I made available on my github account for you to try this out yourself or refer to things as needed.
In my case I chose to create a new Single View Application in XCode such as the following:
-all_load
and -Obj-C
as shown here:
AddressBook.framework
AddressBookUI.framework
AudioToolbox.framework
AVFoundation.framework
CoreLocation.framework
MediaPlayer.framework
QuartzCore.framework
SystemConfiguration.framework
MobileCoreServices.framework
CoreMedia.framework
The result should look like the following:
To actually use the subview, you need to add the following Objective-C code where you want to include it.
Import the following header
#import <Cordova/CDVViewController.h>
Instantiate a new CDVViewController
CDVViewController* viewController = [CDVViewController new];
Set up the view frame
viewController.view.frame = CGRectMake(0, 0, 320, 480);
Lastly, add to your view as a subview
[myView addSubview:viewController.view];
You could optionally set the www folder, start page or option to show the splash screen to something other than the default (default folder is www, start page is index.html and splash screen is no) on that CDVViewController as well with the following lines:
viewController.wwwFolderName = @"myfolder"; viewController.startPage = @"mystartpage.html"; viewController.useSplashScreen = YES;
Here’s an example of actually implementing those lines above in my ViewController.m class that is created with a new native XCode Single View project like discussed above (the added code is in the viewDidLoad
function):
#import "ViewController.h" #import <Cordova/CDVViewController.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CDVViewController* viewController = [CDVViewController new]; viewController.view.frame = CGRectMake(0, 40, 320, 450); [self.view addSubview:viewController.view]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } else { return YES; } } - (void)dealloc { [super dealloc]; } @end
CDVViewController
into our main ViewController
, we could reuse this class anywhere in our application and it will automatically include the Cordova subview. In the storyboard file in XCode (the UI environment), you can actually set the class of any given View Controller object to ViewController and you will get a new Cordova WebView subview component automatically due to this code. You can simply set the class to it if it’s not selected already in the Identity Inspector as shown here:
In my sample application you can see it when you press the Open a new Cordova WebView button. You could choose to add this code wherever you need it, this is just one example for simple illustration.
In the first screenshot below, the header navigator and tab bar at the bottom (with badge), and the second view with all the text and controls are actually native iOS controls, whereas the middle embedded view with the button list is an embedded Cordova WebView containing HTML/JS to interact with the native Cordova/PhoneGap APIs to retrieve contacts, use the camera, etc.
If you’re familiar with Storyboarding in XCode for iOS, the Storyboard shows an overview and flow of the application from the native perspective, so you can see the parts which are iOS Native controls versus the other buttons etc that were included from the HTML/JS as part of the Cordova WebView:
In the next screenshot you can see how we can interact with the Cordova/PhoneGap APIs for native functions like grabbing contacts right there inside our native application:
This next view is all native iOS controls (see storyboard), but includes a button to pop up a new view that uses the same ViewController class and will have the embbeded Cordova subview:
And lastly is our next view from the button pop-up that shows how another view will also have the embedded Cordova WebView:
Grab the sample application from my github account and give it a whirl.
There’s an advanced tutorial dedicated to embedding PhoneGap for Android here, so be sure to check that out too!
Thanks Holly!
I was just looking for this solution and phonegap posted it on their facebook page.
Saved me many an hour of googling.
Hello,
thanks for sharing this. It’s great this is possible. I was wondering if same thing can be achived on Android.
Thanks,
ukw.
Hi there, yes it can, there’s a video here on embedding CordovaWebView with Android with links to a sample project…
Thanks!
Holly
Thanks!
Holly,
We are having some issues getting push messaging to work inside adobe flex mobile. Would it be possible to embeed these phone gap functions inside a flex mobile application.
I am considering converting my app which is written in 100% JavaScript / CSS / HTML5 into more of a hybrid app using this approach. I am wondering if adding a storyboard and swapping views that way (native) would be better performance-wise than swapping views in pure JavaScript (current approach). My main concern would be the amount of time it takes for a Cordova WebView to initialize.
I would be curious to hear your thoughts on this. And thanks for the write-up!
Hi Matthew! I’m not sure I could say 100% myself without trying it. I do think it’s a great question and could make for an interesting approach. I’m going to run this by some of our PG guys to get some feedback and will get back to you ~ Thanks! Holly
Thanks Holly, I’m looking forward to hearing what they have to say!
Hi Holly
I can’t get this to work. I get the following error when I try to run the app in the simulator.
Undefined symbols for architecture i386:
“_OBJC_CLASS_$_CDVViewController”, referenced from:
objc-class-ref in SecondViewController.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Any idea what I’ve done wrong?
Thanks
Tracey
Hi Tracey, what version of XCode and PhoneGap are you using?
Hi Holly
I’m using Xcode Version 4.5 (4G182) and Cordova 2.1.1.
Thanks
Tracey
Tracey – try this:
stackoverflow.com/a/12731951/170237
I also changed the scheme to ‘CordovaLib’ built the project and then switched back to my project scheme.
Hi Holly,
Thanks a lot for this amazing tutorial, It works ok but I believe that the source code in the github is not complete.
Hi there, could you expand on what you mean? Thanks! Holly
Hi Holly,
I’m having trouble making this work, I followed the instructions and I’m able to run the app and get the index.html to showup in Cordova webview, but the “deviceready” is not being fired. No erros reported in console.
I was able to use the command-line tools to build the cordovalib and simulate the app and get deviceready to fire, but when I create my own app, and then add cordovalib project and setup a CDVViewController, it opens index.html and does not fire deviceready, can u help me debug
I am having this same issue… and have been trying to solve the problem since yesterday. It seems that the CDVViewConroller.m class is being initialized incorrectly or it is referenced incorrectly.
The CDVViewConroller.m class never dispatches the “deviceready” event because its “dealloc” method is dispatched almost immediately after loading. Its “webViewDidStartLoad” and “webViewDidFinishLoad” methods are also never called.
I’m new to iOS/phone gap so it could be that the solution is simple, but so far I am stumped.