Mixing Cordova/PhoneGap Components with Native iOS – Part 1

July 22, 2014By 5 Comments

native-embed11

I built this tutorial to serve as a guide to creating a hybrid mobile application combining both native iOS controls and Cordova/PhoneGap components to leverage the best of both worlds as an alternative to picking one over the other. Developers may choose to do this if they want to use specific native UI features but still code the bulk of their business logic in JavaScript or reuse existing JavaScript logic by embedding it as a Cordova component while maintaining the rest of the native scaffolding. There are also benefits to using this approach in making an application feel more native.

Goal

This tutorial takes you through the steps to create a simple hybrid application using both native and Cordova features so you can try it for yourself. The sample application we’ll build here is a basic native iOS application with a Tab Bar for navigation between two views; a native view with a button control, and a custom Cordova view simply showing the default Cordova Device Ready starter application. The point of the sample application is to illustrate how you can combine the two approaches to take advantage of native controls and navigation as well as use JavaScript/HTML within your application where desired. I chose to use the default Cordova app to help quickly illustrate which view is Cordova-specific however you could imagine this being any of your existing PhoneGap/Cordova apps (and using any frameworks like Ionic/AngularJS etc) within it’s own view. Part 2 of this tutorial series covers that example.

I ran across this image below in an article on Smashing Magazine’s site that I think really helps illustrate this true hybrid approach well and highlights the native vs HTML bits (and is also a great read):

voyager-screenshot-shaded_mini2

Background

You may want to skip to Part 1 if you’re already aware of embedding Cordova WebViews in native apps, otherwise I think it’s useful to give a little background on this feature. Many think of PhoneGap/Cordova applications as consisting of a single WebView that has access to all of their device features through the documented APIs and plugins.

A WebView is used to provide HTML content within a native application. The native HTML rendering engine for the platform is then used to present that WebView. The actual class names for WebView’s vary between platforms. In iOS it’s the UIWebView class and WebKit is the native HTML rendering engine (since the Safari browser is based on WebKit).

If you weren’t aware of it already, you can create your own custom Cordova WebViews with all of the device API access and embed them as components within a native application. You are not limited to just one. The original code name for this (using Cordova as a component of your native application) feature was Cleaver and you may see it referenced with that name online or in documentation. PhoneGap/Cordova applications built since v1.4 are all based on Cleaver, but just contain the one Cordova WebView by default. When you create a Cordova/PhoneGap application using the CLI or the Cordova create script, a default XCode template is built and is essentially the reference implementation of a Cordova WebView. You can see the classes that make up Cleaver in the CordovaLib folder within your Cordova/PhoneGap iOS projects:

cleaver-folder

Taking a quick peek into these classes can be helpful with the rest of this post and implementing this feature, even if you’re not an Obj-C developer. You may get other ideas of things you can extend or override further that I’ve not included here for simplicity. Also, pay particular attention to the CDVViewController class. This is the main class used to create custom WebView components. It can be instantiated directly or extended to provide custom functionality.

Note: If you look at the docs for this feature you may wonder why they differ so much from this tutorial, but that angle is assuming you’re starting with a native iOS project and adding Cordova into it whereas this tutorial starts from the Cordova side and adds more native around it since I believe it may target a lot of readers here. I actually blogged about embedding Cordova WebViews from the native side awhile back here and might be worth a read for some more explanation.


Part 1: Getting Started

Requirements

  1. Apache Cordova iOS Project – The cordova-ios source code project is used to create a base iOS project via the cordova create script in the /bin folder.

    Using the cordova create script for iOS is going to net the same result as what you might see when adding the iOS platform using the Cordova/PhoneGap CLI. You don’t want to use the CLI for this scenario though since it would add additional overhead, but more importantly it would overwrite your native code in the iOS platform each time you do a build/run with the CLI. Some of you may be thinking you can just use the CLI and take the project from the platforms/ios folder directly and work with that, which could work, but it’s not the recommended approach in terms of being too easy to overwrite and it complicates things adding plugins etc. It’s best to start with the clean approach using the simple cordova create script in the cordova-ios project.
  2. Plugman – Plugman is a tool used to install plugins outside of the Cordova or PhoneGap CLI (many of the readers of this blog likely have it, but try running plugman quick before you go install it again if you’re not sure). Otherwise, to install it, run:

    $ npm install -g plugman

    If the above gives an error and depending on your setup, you may need to specify sudo with the install command ($ sudo npm install -g plugman).
  3. XCode – XCode 5.1.1 was used for this tutorial.


Part 2: Creating the Project

  • Open Terminal and navigate to the bin folder within the cordova-ios source folder downloaded as the first requirement in Part 1. From the command line within that cordova-ios/bin folder, type the following command to run the create script with the parameter names set to your desired project location, package name and project name respectively:

    ./create ~/NativeCordovaExample io.cordova.example NativeCordovaExample
  • Now navigate into the newly created project folder from the command line and add any necessary plugins via Plugman:

    $ cd ~/NativeCordovaExample
    $ plugman install --platform ios --project . --plugin org.apache.cordova.console

    The above install command installs the console plugin from within your project root (denoted by ‘.’)

    I recommend you install the console plugin at minimum at this point to be sure you can see any debugging statements, but you could use the same syntax to install any other plugins you might need in your own project.
  • stop Now is a good time to stop and make sure this basic application works before taking it further in XCode. From the root of your project at the command line, use the cordova run command to build and run your application such as: $ ./cordova/run. Ensure you see the default Cordova Device Ready view before going further.

    Screen Shot 2014-07-20 at 10.09.50 AM
    It’s assumed you have either an iOS device connected and ready for development or have the iOS Simulator (if you’ve installed ios-sim) installed. Otherwise if you get an error trying to run from the command line this way, the other option is to just open the project in XCode (see next step) and run it there on the simulator.

    Assuming your project was created and you were able to run the default Cordova application, you are now ready to start modifying your XCode project to add some native functionality. Type open from the command line with the .xcodeproj name from your project root (open NativeCordovaExample.xcodeproj) or double-click it from Finder.

    Once you see it open in XCode, you’re ready to move on to Part 3!


Part 3: XCode Storyboarding

Storyboarding Overview

In this section you’re going to start by visually adding native functionality to your basic app using iOS Storyboarding. I personally find this to be the easiest and least intimidating approach for someone not using Obj-C regularly.

A storyboard is the visual representation of all of the screens and their relationships (segues) in an application. Check out this great tutorial on Storyboarding to learn more.

You can actually build a lot of native functionality into your app visually using a Storyboard without writing code, particularly when your approach is to use native handling for things like navigation and transitions versus core business logic. You can still write the bulk of your business logic in JavaScript and use PhoneGap/Cordova APIs but gain a native performance and feel via the navigation/transitions.

Steps

  • Create the Storyboard – Choose File -> New -> File from the menu, then in the next dialog choose the User Interface tab and select the Storyboard option shown below:

    filenew-storyboard
  • Select device target – next you’ll be prompted with a device selection (iPhone or iPad) which basically sets up your Storyboard to reflect one size or the other. (There are ways to convert from one to the other when you’ve completed your app design so don’t worry about it being limited to one device size for now. For the purposes of this tutorial I’m choosing iPhone).

    iphone-device
  • Name the Storyboard – Next you’ll be prompted to name and save your storyboard file. Type in a name like Main, then save within your Classes folder.
  • Set application entry point – The new storyboard file will now serve as the main interface for your application, so before going further you need to update the project properties to point to this new storyboard file as the entry point.

    Select the project root in the Project Navigator on the left and the first tab General, should be selected. Locate the Main Interface drop down and select Main.storyboard. The screenshot below outlines this option in red.

    main-interface

    At this point you may notice the Device Orientations checkboxes below the Main Interface option shows only Portrait selected. If that’s the case and you want to support other orientations for your application then now might be a good time to go ahead and select all that you want to support. This is also where you would set your application icons/start screens and status bar styles so make note of it for when you’re ready to set each of those for your app.
  • Build out the Interface – Now you can begin building the application interface by adding objects to your newly created storyboard file. Click on the Main.storyboard file in the Project Navigator on the left. Ensure the Object Library window is open on the bottom right before proceeding, you’ll need it in the next step. The Object Library is outlined in red in the screenshot below and identified with the little box symbol in the tab icons.

    object-editor

    Tip: If you accidentally close a view, such as the Object Library, you can go to the XCode toolbar and select View->Utilities->Show Object Library. All of the different views can be found within this menu and reopened.

  • Create your first View – Let’s start by dragging a new ViewController object from the Object Library into your storyboard.

    The result of your storyboard should look like this screenshot below:
    viewcontroller

    The ViewController object is mapped to the iOS UIViewController class and automatically contains one UIView as well as several event handlers by default. When you add User Interface controls to a ViewController, you’re really adding them to the View.

    uiviewcontroller_class

    NOTE: I’ll be referring to the UIView and UIViewController as View and ViewController respectively throughout the tutorial since that’s what they’re referred to in the Storyboard and to save confusion.
  • Add a native Button – Select the new View in the Storyboard or from the Document Outline and scroll down into the Object Library again to find the Button object. Drag a Button onto the View and double click on the label to change the text. The result should look like the following:

    native-btn

    Tip: If at any time you’re not quite sure why something isn’t getting selected properly or your dragging of items onto others containers isn’t working as you expect (XCode can be finicky), use the Document Outline window shown below to select it first. I personally like to keep it open so I can always see the hierarchy as well as what’s selected and make sure things are getting put into the right containers. To toggle this outline, click the arrow button circled in red below:


    view-ss

  • stop If you run the app now, you will still see the default Cordova app as before rather than the View and button you added to the Storyboard. This is due to code in the AppDelegate class that was created in the Classes folder with the create script. This class extends the iOS UIApplicationDelegate class and handles application level events while your application is executing (such as when it finished launching). It was automatically created by the create script normally used in default Cordova applications. In this case, you’ll need to make some changes to it though because it’s not being used in the default manner. The ApplicationDelegate class currently hard codes the root view to the MainViewController class which was also created by default, so regardless of what you put in your storyboard or set as your main interface in the properties, this code will override it unless you remove it. Follow the next step to do so.

  • Remove the MainViewController entry point – Open AppDelegate.m and search for didFinishLaunchingWithOptions. In that method you will see the rootViewController for the application is set to the MainViewController class. You’ll want to completely remove everything in this method except the last line return YES;. The result should look like this:
    (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    {
    
        return YES;
    }
    

    When the application runs now, it will defer to the entry point set in the Storyboard indicated by the incoming arrow that was added by default when you added the first ViewController. There is no need to manually code it here. (Refer to the screenshot above to see that incoming arrow).

  • Tip: If at any time you are confused by what you’re supposed to add or remove to the Storyboard or code, remember there’s a sample project using these steps located on my GitHub here for reference.

  • Run it! – Now try to run the application again and you should see the newly created native View and Button instead of the default Cordova application. (Note: the button doesn’t actually do anything yet since no behavior has been set up to handle it yet).

    button-view3
  • Tip: If you want to edit the attributes of the button (or any object on the screen), use the Attribute Inspector in the top right window in XCode as shown below (4th tab over).

    button-attrib

  • Add Tabbed Navigation – In this step you’re going to embed the ViewController added previously to a TabBarController so you can see how to create a simple native application with a Tab Bar for navigation. Go to Editor->Embed In->Tab Bar Controller. You will see that a new TabBarController is now placed on the screen and connected to your ViewController automatically.

    Screen Shot 2014-07-09 at 11.14.45 PM

    First off, notice the incoming entry point arrow was automatically changed to point into the TabBarController. Also notice a new Item label was automatically created and shows up in the bottom tab bar for both. You can double click into that label on the ViewController to change the attributes in the top right Attribute Inspector. Take a moment to go thru the drop-downs and discover the options. In this case let’s supply a custom label and icon for a home tab. The home icon is already included in the sample project, but you could use whatever icon you want as long as you place it in the Resources/Icons folder within the project root. The alternative to a custom icon/label is to select a predefined label/icon pair from commonly used ones in the Identifier drop-down list rather than setting it to Custom.

    attribute-insp

  • Tip: Also locate the magnifying glass icons in the toolbar found right on the Storyboard screen to zoom in and out as you continue adding objects. keyboard


    stopStop and test out the application again to make sure the Home tab is shown in the tab bar as expected like shown below:

    home-tab-sim2

  • Add a Cordova View – Now that you have a basic native view, let’s drag another ViewController onto the Storyboard to serve as the Cordova view. Select it and go into the Identity Inspector tab in the top right window (3rd tab) and change the Custom Class to MainViewController as shown below:

    Screen Shot 2014-07-09 at 11.39.58 PM

    Setting this custom class is tying that visual ViewController object to a class named MainViewController rather than using a default iOS UIViewController. The MainViewController class was created by the create script and serves as the default Cordova View in a basic Cordova/PhoneGap app. The idea is that this Cordova View can be a subview with native tabs, controls and navigation all around it, it doesn’t have to be the whole app itself. You don’t need to add anything to this view because just by setting it to this custom Cordova class, it will display the default Cordova application.

  • Associate the new Cordova View with the TabBarController – To associate the MainViewController with the TabBarController you’ll need to create another link (segue) between the TabBarController and your MainViewController.

    To establish the link between the TabBarController and your new MainViewController, press and hold the Control key and drag from the grey middle area of the TabBarController (NOT the tab bar itself) over to the MainViewController until you see it highlighted in blue. When you let up on the control button you will be presented with a dialog asking which type of segue to add. In this case you’ll want to choose a Relationship Segue for the view controller as shown below:

    Screen Shot 2014-07-10 at 12.49.44 AM

    You may remember the way you associated the first ViewController added was to use the Editor->Embed in Tab Bar Controller option. That was for simplicity and to set the first entry point easily associating it with the first View to be shown. If you did this again with your new ViewController you would just get another separate TabBarController added to the Storyboard tied to that ViewController but not associated with the other.

    You should now see a new tab item added to the tab bar for both. Let’s change the name and icon for this one as well. There’s an icon-40.png file representing a default Cordova icon already within all projects by default, so it’s easy enough to use that icon with a Cordova as the label such as below:

    Screen Shot 2014-07-10 at 7.47.44 PM

    If you run it now you should see both tabs and when you click on the Cordova tab, the default Device Ready app should now be displayed like below.

    cordova-tab-sim2
    Pretty sweet! Actually the app itself is pretty darn ugly right now but it’s easiest to stick to the basics to save confusion and for illustration purposes at this point.

  • Add a native control to the Cordova View – Another cool thing to point out is that you’re not limited to only displaying the Device Ready application in the Cordova View, you can drag any other native controls in to display as well. For instance, drag a native Label object from the Object Editor onto the MainViewController. Set the text to whatever you like and run it again to see what happens. I set mine to Hello from Native as shown below:

    hello-native

    Now when you run your application and click on the Cordova tab, you should see this new native label overlaying the Cordova app:

    native-overlay2

    To understand how this works, it might be helpful to realize the native Label control was added to the View/(UIView) object in the MainViewController. The Cordova WebView displaying the Device Ready app is a subview that was added to that same View object, so it works just fine. You can see the code behind this in the createGapView method in the CDVViewController.m file in the CordovaLib/Classes/Cleaver folder:

    - (void)createGapView
    {
        CGRect webViewBounds = self.view.bounds;
    
        webViewBounds.origin = self.view.bounds.origin;
    
        self.webView = [self newCordovaViewWithFrame:webViewBounds];
        self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
    
        [self.view addSubview:self.webView];
        [self.view sendSubviewToBack:self.webView];
    }
    

Success! With the above steps completed you now have a hybrid application with both a native view and a Cordova view that can be navigated between using iOS navigation. You’ve also seen how you can add additional native controls in the same View containing a custom Cordova View.

The main goal of this post was to lay some groundwork and walk through how a default iOS+Cordova application works. This included introducing some of the classes used on the native side and to show how you can add some native interaction between Cordova Views even if you’re not an Obj-C programmer. In the next part of this tutorial you will add on to the application with another custom Cordova view containing a full-fledged Cordova/Ionic/AngularJS application in addition to your basic Cordova View. I’ll also explain more native methods in the Obj-C code that can be used to add custom functionality to your Views. A 3rd post will cover the communication mechanism between the Cordova JavaScript side and the native iOS so you can handle more advanced functionality if you’re interested in taking this approach further.

The sample application built above can be found on my GitHub account here.

Related Reading

Filed in: CordovaMobile DevelopmentPhoneGap Tags:

About the Author ()

Comments (5)

Trackback URL | Comments RSS Feed

  1. mikek says:

    Fascinating stuff

    Is it actually possible to replace those Prev, Next and Done buttons with a text field?

  2. Raul says:

    great article.

    one problem that i found using the mixed approached was when the cordova view had a longer content that could be scrolled.

    is there a way to keep the cordova view and any native UI elements that are shown on top in sync with regards to scrolling?

  3. Viral Surani says:

    awesome……………
    thank you…………… :)

  4. Red says:

    HI I am trying to customize lables in the address book and i use phonegap. Unable to do so every time it goes to Other. can you help me.

  5. Michael says:

    Hi

    Very nice, I did something similar to this recently using Xamarin.Forms. So the Native was crossplatform with Hybrid. http://www.michaelridland.com/mobile/asp-net-mvc-xamarin-mashups/

    Thanks

    Michael

Leave a Reply