Tutorial: Embed PhoneGap as a Subview in your Native iOS Application

November 15, 2012By 43 Comments

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 :).

Steps to Embed Cordova WebView

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.

  1. Create your base native iOS application in XCode (version 4.5 recommended) unless you just plan to add to an existing native application.
  2. In my case I chose to create a new Single View Application in XCode such as the following:

  3. Download and extract the Cordova source to a permanent folder location on your hard drive (for example: ~/Documents/Cordova).
  4. You’ll need this for copying Cordova resources into your native application.
  5. Close any other Cordova projects and exit XCode (XCode can be quirky, a restart often clears up potential or possible errors).
  6. Navigate to the directory where you put the downloaded Cordova source above and copy the Cordova.plist file into your native application folder on the file system (at same level as the AppDelegate files etc, see picture below). Note: you could also use a Cordova.plist from a newly created Cordova application.

    This file contains specific Cordova property settings and plugins required for your application to use Cordova.
  7. Drag and drop the Cordova.plist file into the Project Navigator of Xcode
  8. Choose the radio-button “Create groups for any added folders”, select the Finish button
  9. Go to the downloaded source from the previous step above and locate the CordovaLib.xcodeproj in the CordovaLib sub-folder. Drag and drop the CordovaLib.xcodeproj file into the Project Navigator of XCode (at the same level as the .plist file copied in above – again choosing ‘Create groups for any added folders‘)
  10. Select CordovaLib.xcodeproj in the Project Navigator
  11. Open the File Inspector with Option-Command-1 or by going to View | Utilities | Show File Inspector
  12. Ensure that it says “Relative to Group” in the File Inspector for the Location drop-down menu similar to below:
  13. Select the project icon in the Project Navigator, select your application under “TARGETS” (should have an A next to it), then select the “Build Settings” tab as shown below:
  14. Locate the setting for “Other Linker Flags” (under the Linking section) and double-click the value text field to bring up the box to enter the flags -all_load and -Obj-C as shown here:
  15. Select the project icon in the Project Navigator, select your target, then select the “Build Phases” tab.
  16. Expand “Link Binaries with Libraries”. Select the “+” button, and add these frameworks (and optionally in the Project Navigator, move them under the Frameworks group):

    AddressBook.framework
    AddressBookUI.framework
    AudioToolbox.framework
    AVFoundation.framework
    CoreLocation.framework
    MediaPlayer.framework
    QuartzCore.framework
    SystemConfiguration.framework
    MobileCoreServices.framework
    CoreMedia.framework

  17. Now expand “Target Dependencies
    Select the “+” button, and add the CordovaLib build product
  18. Expand “Link Binaries with Libraries” again and add libCordova.a
  19. Go to “Xcode Preferences -> Locations -> Derived Data -> Advanced…” and ensure it is set to “Unique
  20. Go back to Build Settings and locate the Header Search Paths and ensure the following properties are set (including the quotes):
    “$(TARGET_BUILD_DIR)/usr/local/lib/include”
    “$(OBJROOT)/UninstalledProducts/include”
    “$(BUILT_PRODUCTS_DIR)”
  21. The result should look like the following:

Code Additions

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
Since we added the above code to create a new 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.

Sample Application

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!

Filed in: CordovaHTML/JSiOSMobile DevelopmentPhoneGap Tags:

About the Author ()

Comments (43)

Trackback URL | Comments RSS Feed

Sites That Link to this Post

  1. How to Add Cordova 2.5 to my existing iOS Native App? | BlogoSfera | March 5, 2013
  1. Adam says:

    Thanks Holly!

    I was just looking for this solution and phonegap posted it on their facebook page.

    Saved me many an hour of googling.

  2. ukw says:

    Hello,
    thanks for sharing this. It’s great this is possible. I was wondering if same thing can be achived on Android.

    Thanks,
    ukw.

  3. brian fruman says:

    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.

  4. Matthew says:

    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

  5. Tracey says:

    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

  6. anquegi says:

    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.

  7. Rakshith says:

    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

    • Chris says:

      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.

    • Tony says:

      Hi, Rakshith

      I also have that problem, it open the index.html, but it does not fire the deviceready event whatever.

      @Holly, I use XCode 4.6 and Phonegap 2.5.0, I am new to phonegap, I super hope if you could have any help for this? Thanks any way!

  8. Mobile dev says:

    Thanks Holly,

    Have you heard about migMobile?

    migMobile – is a cross-platform framework that allows you to build fast and fluid native mobile applications in the language of your choice. We provide a common standardized API across the different platforms/operating systems. The application developed on one can be quickly and easily transferred to the others. In real world applications we have realized on average a 70% reduction in development time required to transfer to subsequent platforms.

    You can find more details at the following link:
    http://www.kickstarter.com/projects/1008546182/migmobile

  9. Marc says:

    Hi Holly,

    I’m trying your tutorial. I am new on native iOS programming, but I have some experience about Phonegap and Cordova Apps. I am mired on Code Additions Section cause I don’t have idea where I have to add this code lines.
    My idea, is make an app with diferents tabs, where sometimes i would use native, and anothers, i would use phonegap and HTML5 code.

  10. Robert says:

    Hi Holly,

    You sample project on GIT is empty i.e. there is no code in there besides what is auto generated by Xcode for a template project.

    Rob

  11. Matt says:

    Hi Holly, thanks for this tutorial. I’d love to download the completed app, but it looks like the GitHub project doesn’t have everything pushed to it.

    For example, the ViewController doesn’t include any Cordova classes: https://github.com/hollyschinsky/NativeAppEmbedWebView/blob/master/NativeAppEmbedWebView/ViewController.m

    Would you be able to push the completed project, including the tab bar? I’m working on a similar problem, and having working code would be really helpful. Thanks!

  12. Sudheer Kumar says:

    Hi Holly,

    Thanks a lot for this amazing tutorial, I had been searching for this example for the past 3 days. I need to embed PhoneGap as a subview in my native single view IOS Application. I have one issue regarding this concept. I need to use Web services url by giving login credentials(View) and get json response (a URL) and need to access that response(url) by using phone gap framework. Do you have any idea regarding this issue (or) any tutorial with you. Thanks in advance…..

  13. Kevin says:

    Hi, I managed to load Cordova 2.5.0 into my embedded ios app running on SDK 6.1. However, during run time on my device, cordova lib is not fully loaded – I cannot get the function document.addEventListener(“deviceready”, deviceInfo, false); to work.

    But yet surprising, even though I cannot call the get_contact function, i still can get get_location function to work.

    I got a run time error: CDVPlugin class CDVSplashScreen (pluginName: splashscreen) does not exist.
    I suspect this is causing the cordova lib not to be fully loaded.

    Any idea how to solve it. Many thanks!

    • Wut says:

      hey Kevin
      go to “config.xml” file
      delete plugin name “SplashScreen” then try to run your project

  14. Jim says:

    The most recent versions of PhoneGap use “config.xml” instead of “Cordova.plist”. Would you follow the same instructions using the XML file?

  15. aki says:

    How to create multiple webviews in Cordova/phonegap architecture ? any pros/ cons using this ? I have done profiling through instrument , didn’t find any leakage, still my app crashes after 30 mins. How to deal with this ?

  16. Olly says:

    HI Holly:
    I’m new to integrating the native app with PhoneGap one. When I was running the GIT project you posted , I got an error finding file. Am I missing something? Thanks in advance

  17. Ismael says:

    Hi Holly,

    Thanks for such an explanatory tutorial.

    I was able to configure this but can’t get it to work as I had expected.

    I have an app built for Android that uses Cordova WebView as main view and I was able to hide a second view which I use to call a javascript function.

    However, for the iOS I can’t seem to do this as the HTML file simply doesn’t call any JavaScript calls I make to the second file but rather re-instantiates the calls to the default script multiple times.

    I also specified which page I wanted loaded in that view but still didn’t work.

    Do you have any suggestions or as I fear, iOS is limited to only one view and sub-views only form sections of this one main view?

    Thanks.

    Ismael

  18. Bueno says:

    Hi Holly!

    How can I run a Cordova application in specific folder app ‘documents’?
    My application has some Cordova View Controller CDVViewController).
    When I change the property wwwFolderName and startPage not working.

    Thanks
    Bueno

  19. Joāo pereira says:

    Hi Holly,

    I’m trying to add a IAd producer files ( Adunity ) into a phonegap view but some functions like ( take pictures ) doesn’t work…
    The error is ( taking pictures only in IOS devices )….

    Can you help me to understand what’s going wrong…

    Thank you

    João P

  20. Naumaan Khan says:

    hi holly using the native view only for transitions would help in improving the transitions?? we’re using jQuery mobile and its transitions are not up to the mark

  21. Aras says:

    Hi Holly, I know long time has passed since you wrote this article, but is there any chance you could provide an update for xcode5? I followed the latest instructions for cordova 3.3.0 but linker option `-Obj-C` is not recognized. I am pretty new to iOS development, so not sure what to do. Here are more details about my issue: http://stackoverflow.com/q/21448428/527559
    I would really appreciate your help.

    • Hi Aras, yes, I would like to do that. I know this is a subject that doesn’t have a lot of information thus far. I hope to get a chance in the near future, let me see what I can do :) Thanks! Holly

  22. madhuiOS says:

    Hi Holly, very nice tutorial i have one small doubt about push notifications, how can i open the specific page in phone gap application when i open the push notification in iOS, currently i am using the pushwoosh services. Thanks

Leave a Reply