spacer

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

Posted by Holly Schinsky on Nov 15, 2012 in Cordova, HTML/JS, iOS, Mobile Development, PhoneGap | 16 comments
spacer

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 spacer .

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:

    spacer

  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.

    spacer

    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. spacer

  14. 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:
  15. spacer

  16. 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:
  17. spacer

  18. Select the project icon in the Project Navigator, select your target, then select the “Build Phases” tab.
  19. 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

  20. spacer

  21. Now expand “Target Dependencies
    Select the “+” button, and add the CordovaLib build product
  22. spacer

  23. Expand “Link Binaries with Libraries” again and add libCordova.a
  24. Go to “Xcode Preferences -> Locations -> Derived Data -> Advanced…” and ensure it is set to “Unique
  25. 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)”
  26. The result should look like the following:
    spacer

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:

spacer

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.

spacer

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:

spacer

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:
spacer

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:

spacer

And lastly is our next view from the button pop-up that shows how another view will also have the embedded Cordova WebView:
spacer

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!

spacer

16 Responses to “Tutorial: Embed PhoneGap as a Subview in your Native iOS Application”

  1. spacer Adam says:
    November 16, 2012 at 8:00 pm

    Thanks Holly!

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

    Saved me many an hour of googling.

    Reply
  2. spacer ukw says:
    November 21, 2012 at 5:35 am

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

    Thanks,
    ukw.

    Reply
    • spacer Holly Schinsky says:
      November 27, 2012 at 10:37 am

      Hi there, yes it can, there’s a video here on embedding CordovaWebView with Android with links to a sample project…

      Thanks!
      Holly

      Reply
      • spacer ukw says:
        November 27, 2012 at 10:41 am

        Thanks!

        Reply
  3. spacer brian fruman says:
    November 23, 2012 at 8:34 am

    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.

    Reply
  4. spacer Matthew says:
    December 10, 2012 at 9:06 pm

    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!

    Reply
    • spacer Holly Schinsky says:
      December 11, 2012 at 1:49 pm

      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

      Reply
      • spacer Matthew says:
        December 11, 2012 at 1:51 pm

        Thanks Holly, I’m looking forward to hearing what they have to say!

        Reply
  5. spacer Tracey says:
    December 12, 2012 at 11:09 am

    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

    Reply
    • spacer Holly Schinsky says:
      December 13, 2012 at 2:54 pm

      Hi Tracey, what version of XCode and PhoneGap are you using?

      Reply
      • spacer Tracey says:
        December 14, 2012 at 5:28 am

        Hi Holly

        I’m using Xcode Version 4.5 (4G182) and Cordova 2.1.1.

        Thanks
        Tracey

        Reply
    • spacer Rich says:
      December 16, 2012 at 2:37 pm

      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.

      Reply
  6. spacer anquegi says:
    December 13, 2012 at 7:48 am

    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.

    Reply
    • spacer Holly Schinsky says:
      December 13, 2012 at 2:55 pm

      Hi there, could you expand on what you mean? Thanks! Holly

      Reply
  7. spacer Rakshith says:
    December 20, 2012 at 12:48 am

    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

    Reply
    • spacer Chris says:
      January 15, 2013 at 1:47 pm

      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.

      Reply

Leave a Reply

Click here to cancel reply.

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.