Book Image

Mastering PhoneGap Mobile Application Development

By : Kerri Shotts
Book Image

Mastering PhoneGap Mobile Application Development

By: Kerri Shotts

Overview of this book

PhoneGap is a useful and flexible tool that enables you to create complex hybrid applications for mobile platforms. In addition to the core technology, there is a large and vibrant community that creates third-party plugins that can take your app to the next level. This book will guide you through the process of creating a complex data-driven hybrid mobile application using PhoneGap, web technologies, and third-party plugins. A good foundation is critical, so you will learn how to create a useful workflow to make development easier. From there, the next version of JavaScript (ES6) and the CSS pre-processor SASS are introduced as a way to simplify creating the look of the mobile application. Responsive design techniques are also covered, including the flexbox layout module. As many apps are data-driven, you'll build an application throughout the course of the book that relies upon IndexedDB and SQLite. You'll also download additional content and address how to handle in-app purchases. Furthermore, you’ll build your own customized plugins for your particular use case. When the app is complete, the book will guide you through the steps necessary to submit your app to the Google Play and Apple iTunes stores.
Table of Contents (19 chapters)
Mastering PhoneGap Mobile Application Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Performing substitutions


Many times, we need to convert certain keywords in a Gulp stream into some other values. A simple example is to transform {{{VERSION}}} your app's version number—for example, into 1.23.4456. Doing this is pretty simple, but it opens up a large number of possibilities.

To do this, we'll use the gulp-replace-task plugin. This plugin will replace all the instances of a particular regular expression with a replacement value. These expressions can become very complex; but in our case, we'll keep them simple.

We'll only need to support substitutions in our code files, so let's create a new task that is designed to copy our code files and apply any necessary substitutions along the way. We'll call it gulp/tasks/copy-code.js.The file should start as follows:

var gulp = require("gulp"),
    replace = require("gulp-replace-task"),
    concat = require("gulp-concat"),
    pkg = require("../../package.json"),
    config = require("../config"),
    paths = require("../utils/paths");

Next, we need to define a method that will perform substitutions on the input streams. Remember, these will be the files matched by the pattern provided to gulp.src():

function performSubstitutions() {
  return replace({
    patterns: [
      {
        match: /{{{VERSION}}}/g,
        replacement: pkg.version
      }
    ]
  });
}

Next, let's define another configuration setting that specifies the code files that do need substitutions and where they should be stored. In gulp/config.js, add a code section to the config.assets object, like this:

assets: {
  copy: [ … ],
  code: {src: "www/js/app/**/*.js", dest: "www/js/app"}
}, …

Next, we need to define the code that will copy the files specified by config.assets.code to the appropriate destination. This will be added to gulp/tasks/copy-code.js, and it should look like this:

function copyCode() {
  return gulp.src([paths.makeFullPath(config.assets.code.src, 
                     paths.SRC)])
             .pipe(performSubstitutions())
             .pipe(concat("app.js"))
             .pipe(gulp.dest(paths.makeFullPath(
                    config.assets.code.dest, paths.DEST)));
}
module.exports = {
    task: copyCode
}

The copyCode method is pretty simple to follow. First, all the JavaScript files are located using the configuration we've specified. These are all passed through performSubstitutions(). The results of the substitutions are then packaged together in a neat little bundle with concat. So, even if we have multiple JavaScript files, they will all be packaged into a single file (app.js).

Note

You don't have to concatenate your files if you don't want to. When you have multiple JavaScript files, however, it means that you have to include each one in your index.html file. Whereas if you bundle them into a single file, you reduce the number of script tags you have in your index.html file.

To test these tasks, we can create two simple files. The first should be placed in src/www/ and named index.html:

<!DOCTYPE html>
<html>
  <head>
    <script src="cordova.js" type="text/javascript"></script>
    <script src="js/app/app.js" type="text/javascript"></script>
  </head>
  <body>
    <p>Hello!</p>
    <div id="demo"></div>
  </body>
</html>

The second file should be in src/www/js/app/ and named index.js:

document.getElementById("demo").textContent = "{{{VERSION}}}";

The JavaScript file itself is very simple, obviously. The idea is simply to prove that our Gulp tasks work. If you execute gulp copy-assets, you'll find that index.html has been copied from src/www/ to build/www/. Likewise, if you execute gulp copy-code, you'll find that index.js has been copied from src/www/js/app/ to build/www/js/app/ and renamed to app.js. If you open the latter file in an editor, you'll also see that {{{VERSION}}} has been replaced with 1.0.0 (which came from package.json).

As you may recall, we indicated earlier in this chapter that we still need a config.xml file. This is true, but we've specified everything we need in package.json. Wouldn't it be great to generate a valid config.xml file from a template? This means that we need more substitutions and a proper template.

Let's define our template first. This should be in src/config.xml (see the code package for the entire file):

<?xml version='1.0' encoding='utf-8'?>
<widget id="{{{ID}}}" version="{{{VERSION}}}"
        xmlns="http://www.w3.org/ns/widgets"
        xmlns:cdv="http://cordova.apache.org/ns/1.0"
        xmlns:gap="http://phonegap.com/ns/1.0">
    <name>{{{NAME}}}</name>
    <description>
      {{{DESCRIPTION}}}
    </description>
    <author email="{{{AUTHOR.EMAIL}}}"href="{{{AUTHOR.SITE}}}">
      {{{AUTHOR.NAME}}}
    </author>
    <content src="index.html" />
      {{{PREFS}}}
    <access origin="*" />
…
</widget>

Notice that there are a lot of substitution variables in the preceding code. Most of them are pretty simple: {{{ID}}}, {{{NAME}}}, and so on. One of them is a little more complex: {{{PREFS}}}. This will need to render our simpler list of preferences in package.json into the XML format required by Cordova.

Let's create a new utility file named gulp/utils/performSubstitutions.js with a new version of the performSubstitutions method. We'll need this new version in two tasks, hence the need to split it out into its own file. The new file should look like this:

var pkg = require("../../package.json"),
    replace = require("gulp-replace-task");
function performSubstitutions() {
  function transformCordovaPrefs() {
    var template = '<preference name="{{{NAME}}}" ' + 
                   'value="{{{VALUE}}}" />';
    if (pkg.cordova &&
      pkg.cordova.preferences instanceof Object) {
      return Object.keys(pkg.cordova.preferences).map(
        function(prefName) {
          var str = template.replace(/{{{NAME}}}/g,
            prefName)
            .replace(/{{{VALUE}}}/g,
              pkg.cordova.preferences[prefName]);
          return str;
        }).join("\n  ");
    }
  }

  return replace({
    patterns: [
      {
        match: /{{{VERSION}}}/g,
        replacement: pkg.version
      },
      {
        match: /{{{ID}}}/g,
        replacement: pkg.cordova.id
      },
      {
        match: /{{{NAME}}}/g,
        replacement: pkg.cordova.name
      },
      {
        match: /{{{DESCRIPTION}}}/g,
        replacement: pkg.cordova.description
      },
      {
        match: /{{{AUTHOR.NAME}}}/g,
        replacement: pkg.cordova.author.name
      },
      {
        match: /{{{AUTHOR.EMAIL}}}/g,
        replacement: pkg.cordova.author.email
      },
      {
        match: /{{{AUTHOR.SITE}}}/g,
        replacement: pkg.cordova.author.site
      },
      {
        match: /{{{PREFS}}}/g,
        replacement: transformCordovaPrefs
      }
    ]
  });
}
module.exports = performSubstitutions;

Next, we'll need to edit gulp/copy-code.js to include this new version. Remove the performSubstitutions method from this file first, and then add the following require to the top of the file:

var …,
  performSubstitutions = require("../utils/performSubstitutions");

Finally, let's add another task that can copy the configuration file. We'll call it gulp/tasks/copy-config.js, and it should look like this:

var gulp = require("gulp"),
    performSubstitutions = 
      require("../utils/performSubstitutions"),
    config = require("../config"),
    paths = require("../utils/paths");

function copyConfig() {
  return gulp.src([paths.makeFullPath("config.xml", paths.SRC)])
             .pipe(performSubstitutions())
             .pipe(gulp.dest(paths.makeFullPath(".", 
               paths.DEST)));
}
module.exports = {
    task: copyConfig
}

Of course, we don't want to have to run lots of individual tasks just to copy files. So let's create a simple task that depends upon these three tasks. By doing so, Gulp will run all of these tasks with a single command.

Let's create the new task with the name gulp/tasks/copy.js. The file should contain the following:

module.exports = {
    deps: ["copy-assets", "copy-config", "copy-code"],
}

This is the shortest task so far. All it does is list the other three tasks as dependencies. This means that they will be executed prior to copy. Since copy doesn't contain any additional code, it's just a simple way to execute several tasks at once. If you execute gulp copy, you'll find that you have a new config.xml file under build. It should look a lot like the following:

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.packtpub.logologyv1" version="1.0.0"
        xmlns="http://www.w3.org/ns/widgets"
        xmlns:cdv="http://cordova.apache.org/ns/1.0"
        xmlns:gap="http://phonegap.com/ns/1.0">
  <name>Logology</name>
  <description>
    Dictionary application for Mastering PhoneGap book
  </description>
  <author email="[email protected]"
   href="http://www.photokandy.com">
    Kerri Shotts
  </author>
  <content src="index.html" />

  <preference name="permissions" value="none" />
  <preference name="fullscreen" value="false" />
  <preference name="orientation" value="default" />
  …
  <access origin="*" />
</widget>

Now that you've mastered the method of performing substitutions, you will learn how to interact with Cordova programmatically in the next section.