Skip to content

Apache Cordova / PhoneGap Cheatsheet

Apache Cordova / PhoneGap - Hybrid Mobile App Development

Apache Cordova (formerly PhoneGap) is a mobile application development framework that enables developers to build native mobile applications using HTML, CSS, and JavaScript. It provides access to native device APIs through JavaScript plugins.

Table of Contents

Installation

Prerequisites

# Install Node.js (version 12 or later)
# Download from nodejs.org

# Verify Node.js installation
node --version
npm --version

# Install Java Development Kit (JDK) 8 or later
# Download from Oracle or use OpenJDK

# Install Android Studio (for Android development)
# Download from developer.android.com

# Install Xcode (for iOS development, macOS only)
# Download from Mac App Store

# Set environment variables
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/platform-tools

Cordova CLI Installation

# Install Cordova CLI globally
npm install -g cordova

# Verify installation
cordova --version

# Check requirements for platforms
cordova requirements

# Install platform-specific tools
# For Android
npm install -g gradle

# For iOS (macOS only)
npm install -g ios-deploy

PhoneGap CLI (Alternative)

# Install PhoneGap CLI (Adobe's distribution)
npm install -g phonegap

# Verify installation
phonegap --version

# PhoneGap Build service (deprecated)
# Use Cordova CLI for new projects

Getting Started

Create New Project

# Create new Cordova project
cordova create MyApp com.example.myapp "My App"

# Navigate to project directory
cd MyApp

# Add platforms
cordova platform add android
cordova platform add ios

# Add plugins
cordova plugin add cordova-plugin-device
cordova plugin add cordova-plugin-camera

# Build the app
cordova build

# Run on device/emulator
cordova run android
cordova run ios

Project Templates

# Create with specific template
cordova create MyApp com.example.myapp "My App" --template hello-world

# Create with custom template
cordova create MyApp com.example.myapp "My App" --template https://github.com/user/template.git

# Create blank project
cordova create MyApp com.example.myapp "My App" --template blank

# Create with TypeScript template
cordova create MyApp com.example.myapp "My App" --template cordova-template-typescript

Project Structure

Basic Structure

MyApp/
├── config.xml           # Cordova configuration
├── package.json         # Node.js dependencies
├── www/                 # Web assets
│   ├── index.html       # Main HTML file
│   ├── css/            # Stylesheets
│   ├── js/             # JavaScript files
│   └── img/            # Images
├── platforms/          # Platform-specific code (generated)
│   ├── android/
│   └── ios/
├── plugins/            # Installed plugins (generated)
├── hooks/              # Build hooks
└── res/                # Resources (icons, splash screens)
    ├── icon/
    └── screen/

www/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <title>My App</title>
    <link rel="stylesheet" type="text/css" href="css/index.css">
</head>
<body>
    <div class="app">
        <h1>Apache Cordova</h1>
        <div id="deviceready" class="blink">
            <p class="event listening">Connecting to Device</p>
            <p class="event received">Device is Ready</p>
        </div>
    </div>
    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
</body>
</html>

www/js/index.js

var app = {
    // Application Constructor
    initialize: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
    },

    // deviceready Event Handler
    onDeviceReady: function() {
        this.receivedEvent('deviceready');

        // Cordova is now initialized. Have fun!
        console.log('Running cordova-' + cordova.platformId + '@' + cordova.version);

        // Example: Get device information
        console.log('Device Model: ' + device.model);
        console.log('Device Platform: ' + device.platform);
        console.log('Device Version: ' + device.version);
    },

    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

app.initialize();

CLI Commands

Project Management

# Create new project
cordova create <path> [id [name [config]]] [options]

# Add platform
cordova platform add <platform-name>
cordova platform add android
cordova platform add ios
cordova platform add browser

# Remove platform
cordova platform remove <platform-name>
cordova platform rm android

# List platforms
cordova platform list
cordova platform ls

# Update platform
cordova platform update <platform-name>

# Check platform version
cordova platform version

Plugin Management

# Add plugin
cordova plugin add <plugin-name>
cordova plugin add cordova-plugin-camera
cordova plugin add cordova-plugin-device

# Add plugin with variables
cordova plugin add cordova-plugin-facebook4 --variable APP_ID="123456789" --variable APP_NAME="myApplication"

# Remove plugin
cordova plugin remove <plugin-name>
cordova plugin rm cordova-plugin-camera

# List plugins
cordova plugin list
cordova plugin ls

# Search plugins
cordova plugin search camera

# Update plugin
cordova plugin update <plugin-name>

Build and Run

# Build for all platforms
cordova build

# Build for specific platform
cordova build android
cordova build ios

# Build with options
cordova build android --release
cordova build ios --device

# Run on emulator
cordova emulate android
cordova emulate ios

# Run on device
cordova run android
cordova run ios

# Run with options
cordova run android --device
cordova run ios --target="iPhone-12"

# Serve for browser testing
cordova serve
cordova serve --port=8080

Information Commands

# Check requirements
cordova requirements

# Get help
cordova help
cordova help platform

# Check version
cordova --version
cordova -v

# Get info about project
cordova info

# List available templates
cordova template list

Platform Management

Android Platform

# Add Android platform
cordova platform add android

# Build for Android
cordova build android

# Run on Android emulator
cordova emulate android

# Run on Android device
cordova run android --device

# Build release APK
cordova build android --release

# Build with specific API level
cordova build android --gradleArg=-PcdvBuildToolsVersion=28.0.3

# Clean Android build
cordova clean android

iOS Platform

# Add iOS platform (macOS only)
cordova platform add ios

# Build for iOS
cordova build ios

# Run on iOS simulator
cordova emulate ios

# Run on iOS device
cordova run ios --device

# Build for specific device
cordova build ios --device

# Build with provisioning profile
cordova build ios --codeSignIdentity="iPhone Developer" --provisioningProfile="UUID"

# Clean iOS build
cordova clean ios

Browser Platform

# Add browser platform
cordova platform add browser

# Run in browser
cordova run browser

# Serve for browser testing
cordova serve

# Build for browser
cordova build browser

Plugin System

Core Plugins

# Device Information
cordova plugin add cordova-plugin-device

# Camera
cordova plugin add cordova-plugin-camera

# File System
cordova plugin add cordova-plugin-file

# Network Information
cordova plugin add cordova-plugin-network-information

# Geolocation
cordova plugin add cordova-plugin-geolocation

# Contacts
cordova plugin add cordova-plugin-contacts

# Media
cordova plugin add cordova-plugin-media

# File Transfer
cordova plugin add cordova-plugin-file-transfer

# InAppBrowser
cordova plugin add cordova-plugin-inappbrowser

# Dialogs
cordova plugin add cordova-plugin-dialogs

# Vibration
cordova plugin add cordova-plugin-vibration

# Battery Status
cordova plugin add cordova-plugin-battery-status

# Splash Screen
cordova plugin add cordova-plugin-splashscreen

# Status Bar
cordova plugin add cordova-plugin-statusbar

# Whitelist
cordova plugin add cordova-plugin-whitelist

Plugin Usage Examples

// Device Plugin
document.addEventListener("deviceready", function() {
    console.log("Device Model: " + device.model);
    console.log("Device Platform: " + device.platform);
    console.log("Device Version: " + device.version);
    console.log("Device UUID: " + device.uuid);
    console.log("Device Cordova: " + device.cordova);
}, false);

// Camera Plugin
function takePicture() {
    var options = {
        quality: 75,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: Camera.PictureSourceType.CAMERA,
        encodingType: Camera.EncodingType.JPEG,
        targetWidth: 300,
        targetHeight: 300
    };

    navigator.camera.getPicture(onSuccess, onFail, options);

    function onSuccess(imageURI) {
        var image = document.getElementById('myImage');
        image.src = imageURI;
    }

    function onFail(message) {
        alert('Failed because: ' + message);
    }
}

// Geolocation Plugin
function getCurrentPosition() {
    var options = {
        enableHighAccuracy: true,
        timeout: 5000,
        maximumAge: 0
    };

    navigator.geolocation.getCurrentPosition(onSuccess, onError, options);

    function onSuccess(position) {
        console.log('Latitude: ' + position.coords.latitude);
        console.log('Longitude: ' + position.coords.longitude);
        console.log('Altitude: ' + position.coords.altitude);
        console.log('Accuracy: ' + position.coords.accuracy);
        console.log('Timestamp: ' + position.timestamp);
    }

    function onError(error) {
        alert('code: ' + error.code + '\n' + 'message: ' + error.message);
    }
}

// File Plugin
function writeFile() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
        fs.root.getFile("test.txt", {create: true, exclusive: false}, function(fileEntry) {
            fileEntry.createWriter(function(fileWriter) {
                fileWriter.onwriteend = function() {
                    console.log("Successful file write...");
                };

                fileWriter.onerror = function(e) {
                    console.log("Failed file write: " + e.toString());
                };

                var dataObj = new Blob(['some file data'], { type: 'text/plain' });
                fileWriter.write(dataObj);
            });
        }, onErrorCreateFile);
    }, onErrorLoadFs);
}

// Network Information Plugin
function checkConnection() {
    var networkState = navigator.connection.type;

    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    alert('Connection type: ' + states[networkState]);
}

Configuration

config.xml

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.example.myapp" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>My App</name>
    <description>
        A sample Apache Cordova application.
    </description>
    <author email="dev@cordova.apache.org" href="http://cordova.io">
        Apache Cordova Team
    </author>

    <!-- Content source -->
    <content src="index.html" />

    <!-- Access control -->
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />

    <!-- Platform-specific configurations -->
    <platform name="android">
        <allow-intent href="market:*" />
        <icon density="ldpi" src="res/icon/android/ldpi.png" />
        <icon density="mdpi" src="res/icon/android/mdpi.png" />
        <icon density="hdpi" src="res/icon/android/hdpi.png" />
        <icon density="xhdpi" src="res/icon/android/xhdpi.png" />
        <icon density="xxhdpi" src="res/icon/android/xxhdpi.png" />
        <icon density="xxxhdpi" src="res/icon/android/xxxhdpi.png" />
        <splash density="land-ldpi" src="res/screen/android/splash-land-ldpi.png" />
        <splash density="land-mdpi" src="res/screen/android/splash-land-mdpi.png" />
        <splash density="land-hdpi" src="res/screen/android/splash-land-hdpi.png" />
        <splash density="land-xhdpi" src="res/screen/android/splash-land-xhdpi.png" />
        <splash density="land-xxhdpi" src="res/screen/android/splash-land-xxhdpi.png" />
        <splash density="land-xxxhdpi" src="res/screen/android/splash-land-xxxhdpi.png" />
        <splash density="port-ldpi" src="res/screen/android/splash-port-ldpi.png" />
        <splash density="port-mdpi" src="res/screen/android/splash-port-mdpi.png" />
        <splash density="port-hdpi" src="res/screen/android/splash-port-hdpi.png" />
        <splash density="port-xhdpi" src="res/screen/android/splash-port-xhdpi.png" />
        <splash density="port-xxhdpi" src="res/screen/android/splash-port-xxhdpi.png" />
        <splash density="port-xxxhdpi" src="res/screen/android/splash-port-xxxhdpi.png" />
    </platform>

    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <icon height="57" src="res/icon/ios/icon.png" width="57" />
        <icon height="114" src="res/icon/ios/icon@2x.png" width="114" />
        <icon height="40" src="res/icon/ios/icon-40.png" width="40" />
        <icon height="80" src="res/icon/ios/icon-40@2x.png" width="80" />
        <icon height="120" src="res/icon/ios/icon-40@3x.png" width="120" />
        <icon height="50" src="res/icon/ios/icon-50.png" width="50" />
        <icon height="100" src="res/icon/ios/icon-50@2x.png" width="100" />
        <icon height="60" src="res/icon/ios/icon-60.png" width="60" />
        <icon height="120" src="res/icon/ios/icon-60@2x.png" width="120" />
        <icon height="180" src="res/icon/ios/icon-60@3x.png" width="180" />
        <icon height="72" src="res/icon/ios/icon-72.png" width="72" />
        <icon height="144" src="res/icon/ios/icon-72@2x.png" width="144" />
        <icon height="76" src="res/icon/ios/icon-76.png" width="76" />
        <icon height="152" src="res/icon/ios/icon-76@2x.png" width="152" />
        <icon height="167" src="res/icon/ios/icon-83.5@2x.png" width="167" />
        <icon height="29" src="res/icon/ios/icon-small.png" width="29" />
        <icon height="58" src="res/icon/ios/icon-small@2x.png" width="58" />
        <icon height="87" src="res/icon/ios/icon-small@3x.png" width="87" />
        <splash height="1136" src="res/screen/ios/Default-568h@2x~iphone.png" width="640" />
        <splash height="1334" src="res/screen/ios/Default-667h.png" width="750" />
        <splash height="2208" src="res/screen/ios/Default-736h.png" width="1242" />
        <splash height="1242" src="res/screen/ios/Default-Landscape-736h.png" width="2208" />
        <splash height="1536" src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" />
        <splash height="2048" src="res/screen/ios/Default-Landscape@~ipadpro.png" width="2732" />
        <splash height="768" src="res/screen/ios/Default-Landscape~ipad.png" width="1024" />
        <splash height="2048" src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" />
        <splash height="2732" src="res/screen/ios/Default-Portrait@~ipadpro.png" width="2048" />
        <splash height="1024" src="res/screen/ios/Default-Portrait~ipad.png" width="768" />
        <splash height="960" src="res/screen/ios/Default@2x~iphone.png" width="640" />
        <splash height="480" src="res/screen/ios/Default~iphone.png" width="320" />
        <splash height="2732" src="res/screen/ios/Default@2x~universal~anyany.png" width="2732" />
    </platform>

    <!-- Preferences -->
    <preference name="DisallowOverscroll" value="true" />
    <preference name="android-minSdkVersion" value="19" />
    <preference name="BackupWebStorage" value="none" />
    <preference name="SplashMaintainAspectRatio" value="true" />
    <preference name="FadeSplashScreenDuration" value="300" />
    <preference name="SplashShowOnlyFirstTime" value="false" />
    <preference name="SplashScreen" value="screen" />
    <preference name="SplashScreenDelay" value="3000" />

    <!-- Plugin configurations -->
    <plugin name="cordova-plugin-whitelist" spec="1" />
    <plugin name="cordova-plugin-statusbar" spec="2" />
    <plugin name="cordova-plugin-device" spec="2" />
    <plugin name="cordova-plugin-splashscreen" spec="5" />
    <plugin name="cordova-plugin-ionic-webview" spec="^4.0.0" />
    <plugin name="cordova-plugin-ionic-keyboard" spec="^2.0.5" />
</widget>

Platform-specific Preferences

<!-- Android-specific preferences -->
<platform name="android">
    <preference name="android-minSdkVersion" value="19" />
    <preference name="android-targetSdkVersion" value="28" />
    <preference name="android-installLocation" value="auto" />
    <preference name="Orientation" value="portrait" />
    <preference name="Fullscreen" value="false" />
    <preference name="KeepRunning" value="true" />
    <preference name="LoadUrlTimeoutValue" value="20000" />
    <preference name="SplashScreen" value="splash" />
    <preference name="SplashScreenDelay" value="3000" />
    <preference name="InAppBrowserStorageEnabled" value="true" />
    <preference name="LoadingDialog" value="My Title,My Message" />
    <preference name="ErrorUrl" value="myErrorPage.html" />
    <preference name="ShowTitle" value="true" />
    <preference name="LogLevel" value="VERBOSE" />
</platform>

<!-- iOS-specific preferences -->
<platform name="ios">
    <preference name="CordovaWebViewEngine" value="CDVUIWebViewEngine" />
    <preference name="MinimumOSVersion" value="10.0" />
    <preference name="AllowInlineMediaPlayback" value="false" />
    <preference name="BackupWebStorage" value="none" />
    <preference name="TopActivityIndicator" value="gray" />
    <preference name="EnableViewportScale" value="false" />
    <preference name="KeyboardDisplayRequiresUserAction" value="true" />
    <preference name="SuppressesIncrementalRendering" value="false" />
    <preference name="SuppressesLongPressGesture" value="false" />
    <preference name="Suppresses3DTouchGesture" value="false" />
    <preference name="GapBetweenPages" value="0" />
    <preference name="PageLength" value="0" />
    <preference name="PaginationBreakingMode" value="page" />
    <preference name="PaginationMode" value="unpaginated" />
</platform>

Device APIs

Camera API

// Take picture from camera
function capturePhoto() {
    var options = {
        quality: 50,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: Camera.PictureSourceType.CAMERA,
        encodingType: Camera.EncodingType.JPEG,
        targetWidth: 300,
        targetHeight: 300,
        mediaType: Camera.MediaType.PICTURE,
        allowEdit: true,
        correctOrientation: true
    };

    navigator.camera.getPicture(onPhotoSuccess, onPhotoError, options);
}

// Select picture from gallery
function selectPhoto() {
    var options = {
        quality: 50,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
        mediaType: Camera.MediaType.PICTURE,
        allowEdit: true,
        encodingType: Camera.EncodingType.JPEG,
        targetWidth: 300,
        targetHeight: 300
    };

    navigator.camera.getPicture(onPhotoSuccess, onPhotoError, options);
}

function onPhotoSuccess(imageURI) {
    var image = document.getElementById('myImage');
    image.src = imageURI;
}

function onPhotoError(message) {
    alert('Failed because: ' + message);
}

// Cleanup camera resources
function cleanup() {
    navigator.camera.cleanup(onSuccess, onFail);

    function onSuccess() {
        console.log("Camera cleanup success.")
    }

    function onFail(message) {
        alert('Failed because: ' + message);
    }
}

Geolocation API

// Get current position
function getCurrentLocation() {
    var options = {
        enableHighAccuracy: true,
        timeout: 10000,
        maximumAge: 60000
    };

    navigator.geolocation.getCurrentPosition(onLocationSuccess, onLocationError, options);
}

// Watch position changes
var watchID = null;

function watchLocation() {
    var options = {
        enableHighAccuracy: true,
        timeout: 30000,
        maximumAge: 60000
    };

    watchID = navigator.geolocation.watchPosition(onLocationSuccess, onLocationError, options);
}

function stopWatching() {
    if (watchID != null) {
        navigator.geolocation.clearWatch(watchID);
        watchID = null;
    }
}

function onLocationSuccess(position) {
    var element = document.getElementById('geolocation');
    element.innerHTML = 'Latitude: ' + position.coords.latitude + '<br />' +
                        'Longitude: ' + position.coords.longitude + '<br />' +
                        'Altitude: ' + position.coords.altitude + '<br />' +
                        'Accuracy: ' + position.coords.accuracy + '<br />' +
                        'Altitude Accuracy: ' + position.coords.altitudeAccuracy + '<br />' +
                        'Heading: ' + position.coords.heading + '<br />' +
                        'Speed: ' + position.coords.speed + '<br />' +
                        'Timestamp: ' + position.timestamp + '<br />';
}

function onLocationError(error) {
    alert('code: ' + error.code + '\n' + 'message: ' + error.message + '\n');
}

File System API

// Request file system
function requestFileSystem() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, onFileSystemError);
}

function onFileSystemSuccess(fileSystem) {
    console.log(fileSystem.name);
    console.log(fileSystem.root.name);
}

function onFileSystemError(error) {
    console.log(error.code);
}

// Create file
function createFile() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
        fs.root.getFile("newFile.txt", {create: true, exclusive: false}, function(fileEntry) {
            console.log("File created: " + fileEntry.fullPath);
        }, onErrorCreateFile);
    }, onErrorLoadFs);
}

// Write to file
function writeToFile() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
        fs.root.getFile("newFile.txt", {create: true, exclusive: false}, function(fileEntry) {
            fileEntry.createWriter(function(fileWriter) {
                fileWriter.onwriteend = function() {
                    console.log("Successful file write...");
                    readFile();
                };

                fileWriter.onerror = function(e) {
                    console.log("Failed file write: " + e.toString());
                };

                var dataObj = new Blob(['some file data'], { type: 'text/plain' });
                fileWriter.write(dataObj);
            });
        }, onErrorCreateFile);
    }, onErrorLoadFs);
}

// Read file
function readFile() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
        fs.root.getFile("newFile.txt", {}, function(fileEntry) {
            fileEntry.file(function(file) {
                var reader = new FileReader();

                reader.onloadend = function() {
                    console.log("Successful file read: " + this.result);
                    displayFileData(fileEntry.fullPath + ": " + this.result);
                };

                reader.readAsText(file);
            }, onErrorReadFile);
        }, onErrorCreateFile);
    }, onErrorLoadFs);
}

// Delete file
function deleteFile() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
        fs.root.getFile("newFile.txt", {create: false}, function(fileEntry) {
            fileEntry.remove(function() {
                console.log("File deleted");
            }, onErrorDeleteFile);
        }, onErrorCreateFile);
    }, onErrorLoadFs);
}

function onErrorLoadFs(error) {
    console.log(error.code);
}

function onErrorCreateFile(error) {
    console.log(error.code);
}

function onErrorReadFile(error) {
    console.log(error.code);
}

function onErrorDeleteFile(error) {
    console.log(error.code);
}

function displayFileData(data) {
    document.getElementById("fileData").innerHTML = data;
}

Contacts API

// Create contact
function createContact() {
    var contact = navigator.contacts.create();
    contact.displayName = "Plumber";
    contact.nickname = "Plumber";

    var name = new ContactName();
    name.givenName = "Jane";
    name.familyName = "Doe";
    contact.name = name;

    var phoneNumbers = [];
    phoneNumbers[0] = new ContactField('work', '212-555-1234', false);
    phoneNumbers[1] = new ContactField('mobile', '917-555-5432', true);
    contact.phoneNumbers = phoneNumbers;

    var emails = [];
    emails[0] = new ContactField('work', 'jane_doe@company.com', false);
    contact.emails = emails;

    contact.save(onContactSaveSuccess, onContactSaveError);
}

function onContactSaveSuccess(contact) {
    alert("Save Success");
}

function onContactSaveError(contactError) {
    alert("Error = " + contactError.code);
}

// Find contacts
function findContacts() {
    var options = new ContactFindOptions();
    options.filter = "Bob";
    options.multiple = true;
    var fields = ["displayName", "name"];
    navigator.contacts.find(fields, onContactFindSuccess, onContactFindError, options);
}

function onContactFindSuccess(contacts) {
    for (var i = 0; i < contacts.length; i++) {
        console.log("Display Name = " + contacts[i].displayName);
    }
}

function onContactFindError(contactError) {
    alert('onError!');
}

// Clone contact
function cloneContact() {
    var clone = contact.clone();
    clone.name.givenName = "John";
    clone.save(onContactSaveSuccess, onContactSaveError);
}

// Remove contact
function removeContact() {
    contact.remove(onContactRemoveSuccess, onContactRemoveError);
}

function onContactRemoveSuccess() {
    alert("Removal Success");
}

function onContactRemoveError(contactError) {
    alert("Error = " + contactError.code);
}

UI Frameworks

jQuery Mobile Integration

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>jQuery Mobile App</title>
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
    <div data-role="page" id="home">
        <div data-role="header">
            <h1>My App</h1>
        </div>
        <div data-role="content">
            <ul data-role="listview" data-inset="true">
                <li><a href="#page2">Page 2</a></li>
                <li><a href="#page3">Page 3</a></li>
            </ul>
            <button onclick="takePicture()">Take Picture</button>
        </div>
        <div data-role="footer">
            <h4>Footer</h4>
        </div>
    </div>

    <div data-role="page" id="page2">
        <div data-role="header">
            <a href="#home" data-icon="arrow-l">Back</a>
            <h1>Page 2</h1>
        </div>
        <div data-role="content">
            <p>This is page 2</p>
        </div>
    </div>

    <script src="cordova.js"></script>
    <script src="js/index.js"></script>
</body>
</html>

Framework7 Integration

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <title>Framework7 App</title>
    <link rel="stylesheet" href="lib/framework7/css/framework7.bundle.min.css">
    <link rel="stylesheet" href="css/app.css">
</head>
<body>
    <div id="app">
        <div class="view view-main view-init" data-url="/">
            <div class="page" data-name="home">
                <div class="navbar">
                    <div class="navbar-bg"></div>
                    <div class="navbar-inner">
                        <div class="title">My App</div>
                    </div>
                </div>
                <div class="page-content">
                    <div class="block">
                        <p>Welcome to my Cordova app with Framework7!</p>
                        <a class="button button-fill" onclick="takePicture()">Take Picture</a>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="lib/framework7/js/framework7.bundle.min.js"></script>
    <script src="cordova.js"></script>
    <script src="js/app.js"></script>
</body>
</html>

Ionic Framework Integration

# Install Ionic CLI
npm install -g @ionic/cli

# Create Ionic Cordova project
ionic start myApp tabs --type=angular --cordova

# Add platforms
ionic cordova platform add android
ionic cordova platform add ios

# Add plugins
ionic cordova plugin add cordova-plugin-camera

# Build and run
ionic cordova build android
ionic cordova run android

Development Workflow

Live Reload

# Install live reload plugin
cordova plugin add cordova-plugin-browsersync

# Run with live reload
cordova run android --live-reload
cordova run ios --live-reload

# Serve for browser development
cordova serve
cordova serve --port=8080

# Use browser platform for quick testing
cordova platform add browser
cordova run browser

Hot Code Push

# Install CodePush plugin
cordova plugin add cordova-plugin-code-push

# Configure CodePush
# Add to config.xml:
# <preference name="CodePushDeploymentKey" value="YOUR_DEPLOYMENT_KEY" />

# Release update
code-push release-cordova myApp-Android www 1.0.0
code-push release-cordova myApp-iOS www 1.0.0

Build Hooks

// hooks/before_build/010_add_platform_class.js
module.exports = function(context) {
    var fs = require('fs');
    var path = require('path');

    var platformsMap = {
        'android': 'android',
        'ios': 'ios',
        'browser': 'browser'
    };

    var platforms = context.opts.cordova.platforms;

    platforms.forEach(function(platform) {
        var indexPath = path.join(context.opts.projectRoot, 'www', 'index.html');
        var indexContent = fs.readFileSync(indexPath, 'utf8');

        // Add platform-specific class to body
        var platformClass = platformsMap[platform];
        if (platformClass) {
            indexContent = indexContent.replace(
                /<body([^>]*)>/,
                '<body$1 class="platform-' + platformClass + '">'
            );

            fs.writeFileSync(indexPath, indexContent);
        }
    });
};

Environment Configuration

// js/config.js
var Config = {
    development: {
        apiUrl: 'http://localhost:3000/api',
        debug: true
    },
    production: {
        apiUrl: 'https://api.myapp.com',
        debug: false
    }
};

// Detect environment
var environment = 'development';
if (window.location.protocol === 'file:') {
    environment = 'production';
}

var config = Config[environment];

// Usage
console.log('API URL:', config.apiUrl);
console.log('Debug mode:', config.debug);

Building and Testing

Debug Builds

# Build debug version
cordova build android
cordova build ios

# Run debug on device
cordova run android --device --debug
cordova run ios --device --debug

# Enable debugging
cordova build android --debug
cordova build ios --debug

Release Builds

# Build release version
cordova build android --release
cordova build ios --release

# Sign Android APK
# Generate keystore
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

# Sign APK
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk alias_name

# Align APK
zipalign -v 4 platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk MyApp.apk

Automated Testing

// test/spec/test.js
describe('App Tests', function() {
    beforeEach(function(done) {
        document.addEventListener('deviceready', done, false);
    });

    it('should have device plugin', function() {
        expect(window.device).toBeDefined();
        expect(device.platform).toBeDefined();
    });

    it('should have camera plugin', function() {
        expect(navigator.camera).toBeDefined();
        expect(navigator.camera.getPicture).toBeDefined();
    });

    it('should take picture', function(done) {
        var options = {
            quality: 50,
            destinationType: Camera.DestinationType.FILE_URI,
            sourceType: Camera.PictureSourceType.CAMERA
        };

        navigator.camera.getPicture(
            function(imageURI) {
                expect(imageURI).toBeDefined();
                done();
            },
            function(error) {
                fail('Camera error: ' + error);
                done();
            },
            options
        );
    });
});

// Run tests with Jasmine
// Include jasmine.js and jasmine-html.js in test.html

Debugging

Remote Debugging

# Chrome DevTools for Android
# 1. Enable USB debugging on Android device
# 2. Connect device to computer
# 3. Open Chrome and go to chrome://inspect
# 4. Select your app from the list

# Safari Web Inspector for iOS
# 1. Enable Web Inspector on iOS device (Settings > Safari > Advanced)
# 2. Connect device to Mac
# 3. Open Safari > Develop > [Device Name] > [App Name]

# Weinre (Web Inspector Remote)
npm install -g weinre
weinre --boundHost 0.0.0.0 --httpPort 8080

# Add to index.html:
# <script src="http://YOUR_IP:8080/target/target-script-min.js#anonymous"></script>

Console Logging

// Enhanced console logging
var Logger = {
    log: function(message, data) {
        if (window.console) {
            console.log('[LOG] ' + message, data || '');
        }
        this.writeToFile('LOG', message, data);
    },

    error: function(message, error) {
        if (window.console) {
            console.error('[ERROR] ' + message, error || '');
        }
        this.writeToFile('ERROR', message, error);
    },

    warn: function(message, data) {
        if (window.console) {
            console.warn('[WARN] ' + message, data || '');
        }
        this.writeToFile('WARN', message, data);
    },

    writeToFile: function(level, message, data) {
        // Write logs to file for later analysis
        var logEntry = new Date().toISOString() + ' [' + level + '] ' + message;
        if (data) {
            logEntry += ' ' + JSON.stringify(data);
        }

        // Implementation depends on file plugin
        // this.appendToLogFile(logEntry);
    }
};

// Usage
Logger.log('App started');
Logger.error('Network error', error);
Logger.warn('Low battery', batteryLevel);

Error Handling

// Global error handler
window.onerror = function(message, source, lineno, colno, error) {
    Logger.error('Global error: ' + message, {
        source: source,
        line: lineno,
        column: colno,
        error: error
    });

    // Send error to analytics service
    // Analytics.trackError(message, source, lineno);

    return true; // Prevent default browser error handling
};

// Unhandled promise rejection handler
window.addEventListener('unhandledrejection', function(event) {
    Logger.error('Unhandled promise rejection', event.reason);
    event.preventDefault();
});

// Cordova-specific error handling
document.addEventListener('deviceready', function() {
    // Handle plugin errors
    if (window.plugins && window.plugins.toast) {
        window.plugins.toast.showShortTop = function(message) {
            try {
                window.plugins.toast.show(message, 'short', 'top');
            } catch (error) {
                Logger.error('Toast plugin error', error);
                alert(message); // Fallback
            }
        };
    }
}, false);

Performance Optimization

Memory Management

// Efficient DOM manipulation
var DOMUtils = {
    createElement: function(tag, attributes, content) {
        var element = document.createElement(tag);

        if (attributes) {
            for (var attr in attributes) {
                element.setAttribute(attr, attributes[attr]);
            }
        }

        if (content) {
            element.innerHTML = content;
        }

        return element;
    },

    removeElement: function(element) {
        if (element && element.parentNode) {
            element.parentNode.removeChild(element);
        }
    },

    clearElement: function(element) {
        while (element.firstChild) {
            element.removeChild(element.firstChild);
        }
    }
};

// Memory-efficient list rendering
var ListView = {
    render: function(container, items, itemRenderer) {
        // Clear existing content
        DOMUtils.clearElement(container);

        // Use document fragment for efficient DOM updates
        var fragment = document.createDocumentFragment();

        items.forEach(function(item, index) {
            var itemElement = itemRenderer(item, index);
            fragment.appendChild(itemElement);
        });

        container.appendChild(fragment);
    },

    renderVirtual: function(container, items, itemRenderer, visibleCount) {
        // Virtual scrolling for large lists
        var startIndex = 0;
        var endIndex = Math.min(visibleCount, items.length);

        DOMUtils.clearElement(container);

        for (var i = startIndex; i < endIndex; i++) {
            var itemElement = itemRenderer(items[i], i);
            container.appendChild(itemElement);
        }
    }
};

Image Optimization

// Lazy loading images
var ImageLoader = {
    loadImage: function(src, callback) {
        var img = new Image();

        img.onload = function() {
            callback(null, img);
        };

        img.onerror = function() {
            callback(new Error('Failed to load image: ' + src));
        };

        img.src = src;
    },

    resizeImage: function(file, maxWidth, maxHeight, quality, callback) {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        var img = new Image();

        img.onload = function() {
            var ratio = Math.min(maxWidth / img.width, maxHeight / img.height);
            canvas.width = img.width * ratio;
            canvas.height = img.height * ratio;

            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

            canvas.toBlob(callback, 'image/jpeg', quality);
        };

        img.src = URL.createObjectURL(file);
    },

    preloadImages: function(urls, callback) {
        var loaded = 0;
        var total = urls.length;
        var images = [];

        if (total === 0) {
            callback(null, images);
            return;
        }

        urls.forEach(function(url, index) {
            this.loadImage(url, function(error, img) {
                if (error) {
                    callback(error);
                    return;
                }

                images[index] = img;
                loaded++;

                if (loaded === total) {
                    callback(null, images);
                }
            });
        }.bind(this));
    }
};

Network Optimization

// Network request manager
var NetworkManager = {
    cache: {},

    request: function(url, options, callback) {
        options = options || {};

        // Check cache first
        if (options.cache && this.cache[url]) {
            callback(null, this.cache[url]);
            return;
        }

        var xhr = new XMLHttpRequest();

        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    var response = xhr.responseText;

                    try {
                        response = JSON.parse(response);
                    } catch (e) {
                        // Response is not JSON
                    }

                    // Cache successful responses
                    if (options.cache) {
                        this.cache[url] = response;
                    }

                    callback(null, response);
                } else {
                    callback(new Error('HTTP ' + xhr.status + ': ' + xhr.statusText));
                }
            }
        }.bind(this);

        xhr.open(options.method || 'GET', url);

        // Set headers
        if (options.headers) {
            for (var header in options.headers) {
                xhr.setRequestHeader(header, options.headers[header]);
            }
        }

        xhr.send(options.body || null);
    },

    clearCache: function() {
        this.cache = {};
    },

    // Batch requests
    batch: function(requests, callback) {
        var completed = 0;
        var results = [];
        var hasError = false;

        requests.forEach(function(request, index) {
            this.request(request.url, request.options, function(error, response) {
                if (hasError) return;

                if (error) {
                    hasError = true;
                    callback(error);
                    return;
                }

                results[index] = response;
                completed++;

                if (completed === requests.length) {
                    callback(null, results);
                }
            });
        }.bind(this));
    }
};

Security

Content Security Policy

<!-- Add to index.html -->
<meta http-equiv="Content-Security-Policy" content="
    default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval';
    style-src 'self' 'unsafe-inline';
    media-src *;
    img-src 'self' data: content:;
    connect-src 'self' https://api.myapp.com;
    script-src 'self' 'unsafe-inline' 'unsafe-eval';
">

Secure Storage

// Secure data storage
var SecureStorage = {
    encrypt: function(data, key) {
        // Use a proper encryption library like CryptoJS
        return CryptoJS.AES.encrypt(JSON.stringify(data), key).toString();
    },

    decrypt: function(encryptedData, key) {
        try {
            var bytes = CryptoJS.AES.decrypt(encryptedData, key);
            var decryptedData = bytes.toString(CryptoJS.enc.Utf8);
            return JSON.parse(decryptedData);
        } catch (error) {
            throw new Error('Failed to decrypt data');
        }
    },

    store: function(key, data, password) {
        var encryptedData = this.encrypt(data, password);
        localStorage.setItem(key, encryptedData);
    },

    retrieve: function(key, password) {
        var encryptedData = localStorage.getItem(key);
        if (!encryptedData) {
            return null;
        }

        return this.decrypt(encryptedData, password);
    },

    remove: function(key) {
        localStorage.removeItem(key);
    }
};

// Usage
SecureStorage.store('userToken', { token: 'abc123' }, 'myPassword');
var userData = SecureStorage.retrieve('userToken', 'myPassword');

Input Validation

// Input validation utilities
var Validator = {
    email: function(email) {
        var regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    },

    phone: function(phone) {
        var regex = /^\+?[\d\s\-\(\)]+$/;
        return regex.test(phone) && phone.replace(/\D/g, '').length >= 10;
    },

    url: function(url) {
        try {
            new URL(url);
            return true;
        } catch (error) {
            return false;
        }
    },

    sanitize: function(input) {
        return input.replace(/[<>'"&]/g, function(match) {
            var entities = {
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#x27;',
                '&': '&amp;'
            };
            return entities[match];
        });
    },

    length: function(input, min, max) {
        return input.length >= min && input.length <= max;
    },

    required: function(input) {
        return input !== null && input !== undefined && input.toString().trim() !== '';
    }
};

// Form validation
var FormValidator = {
    validate: function(form, rules) {
        var errors = {};

        for (var field in rules) {
            var value = form[field];
            var fieldRules = rules[field];

            for (var rule in fieldRules) {
                var ruleValue = fieldRules[rule];
                var isValid = false;

                switch (rule) {
                    case 'required':
                        isValid = ruleValue ? Validator.required(value) : true;
                        break;
                    case 'email':
                        isValid = ruleValue ? Validator.email(value) : true;
                        break;
                    case 'minLength':
                        isValid = value ? value.length >= ruleValue : true;
                        break;
                    case 'maxLength':
                        isValid = value ? value.length <= ruleValue : true;
                        break;
                }

                if (!isValid) {
                    errors[field] = errors[field] || [];
                    errors[field].push(rule);
                }
            }
        }

        return {
            isValid: Object.keys(errors).length === 0,
            errors: errors
        };
    }
};

// Usage
var formData = {
    email: 'user@example.com',
    password: '123456',
    name: 'John Doe'
};

var rules = {
    email: { required: true, email: true },
    password: { required: true, minLength: 6 },
    name: { required: true, maxLength: 50 }
};

var validation = FormValidator.validate(formData, rules);
if (!validation.isValid) {
    console.log('Validation errors:', validation.errors);
}

Deployment

App Store Deployment

# iOS App Store
# 1. Build release version
cordova build ios --release

# 2. Open Xcode project
open platforms/ios/MyApp.xcworkspace

# 3. Configure signing and provisioning
# 4. Archive and upload to App Store Connect

# Google Play Store
# 1. Generate signed APK
cordova build android --release

# 2. Sign APK with keystore
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk alias_name

# 3. Align APK
zipalign -v 4 platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk MyApp.apk

# 4. Upload to Google Play Console

Enterprise Distribution

# iOS Enterprise Distribution
# 1. Build with enterprise certificate
cordova build ios --release

# 2. Create IPA file
# 3. Host on internal server with manifest.plist

# Android Enterprise Distribution
# 1. Build signed APK
cordova build android --release

# 2. Distribute through MDM or direct download

Continuous Integration

# .github/workflows/build.yml
name: Build App

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'

    - name: Install dependencies
      run: npm install

    - name: Install Cordova
      run: npm install -g cordova

    - name: Add platforms
      run: |
        cordova platform add android
        cordova platform add ios

    - name: Build Android
      run: cordova build android

    - name: Build iOS
      run: cordova build ios

    - name: Run tests
      run: npm test

    - name: Upload artifacts
      uses: actions/upload-artifact@v2
      with:
        name: builds
        path: platforms/*/build/

Best Practices

Project Organization

src/
├── js/
│   ├── app.js              # Main application logic
│   ├── config.js           # Configuration
│   ├── utils.js            # Utility functions
│   ├── services/           # API services
│   ├── models/             # Data models
│   └── views/              # View controllers
├── css/
│   ├── app.css             # Main styles
│   ├── components/         # Component styles
│   └── themes/             # Theme styles
├── templates/              # HTML templates
├── assets/
│   ├── images/
│   ├── fonts/
│   └── sounds/
└── tests/                  # Test files

Code Quality

// Use strict mode
'use strict';

// Namespace your code
var MyApp = MyApp || {};

MyApp.Utils = {
    // Utility functions
};

MyApp.Services = {
    // Service functions
};

MyApp.Views = {
    // View controllers
};

// Use consistent error handling
MyApp.ErrorHandler = {
    handle: function(error, context) {
        console.error('Error in ' + context + ':', error);

        // Log to analytics service
        if (MyApp.Analytics) {
            MyApp.Analytics.trackError(error, context);
        }

        // Show user-friendly message
        if (navigator.notification) {
            navigator.notification.alert(
                'An error occurred. Please try again.',
                null,
                'Error',
                'OK'
            );
        } else {
            alert('An error occurred. Please try again.');
        }
    }
};

// Use consistent coding style
var MyApp = {
    init: function() {
        this.bindEvents();
        this.setupUI();
    },

    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
        document.addEventListener('pause', this.onPause.bind(this), false);
        document.addEventListener('resume', this.onResume.bind(this), false);
    },

    onDeviceReady: function() {
        console.log('Device ready');
        this.setupPlugins();
    },

    onPause: function() {
        console.log('App paused');
        this.saveState();
    },

    onResume: function() {
        console.log('App resumed');
        this.restoreState();
    },

    setupPlugins: function() {
        // Initialize plugins
    },

    setupUI: function() {
        // Setup user interface
    },

    saveState: function() {
        // Save application state
    },

    restoreState: function() {
        // Restore application state
    }
};

Performance Guidelines

// Optimize for mobile performance
var PerformanceOptimizer = {
    // Debounce function calls
    debounce: function(func, wait) {
        var timeout;
        return function() {
            var context = this;
            var args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(function() {
                func.apply(context, args);
            }, wait);
        };
    },

    // Throttle function calls
    throttle: function(func, limit) {
        var inThrottle;
        return function() {
            var args = arguments;
            var context = this;
            if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(function() {
                    inThrottle = false;
                }, limit);
            }
        };
    },

    // Optimize scroll events
    optimizeScroll: function(element, callback) {
        var ticking = false;

        function update() {
            callback();
            ticking = false;
        }

        function requestTick() {
            if (!ticking) {
                requestAnimationFrame(update);
                ticking = true;
            }
        }

        element.addEventListener('scroll', requestTick);
    },

    // Lazy load content
    lazyLoad: function(elements, callback) {
        var observer = new IntersectionObserver(function(entries) {
            entries.forEach(function(entry) {
                if (entry.isIntersecting) {
                    callback(entry.target);
                    observer.unobserve(entry.target);
                }
            });
        });

        elements.forEach(function(element) {
            observer.observe(element);
        });
    }
};

Summary

Apache Cordova/PhoneGap provides a powerful platform for hybrid mobile app development:

Advantages: - Cross-platform: Write once, run on multiple platforms - Web Technologies: Use familiar HTML, CSS, and JavaScript - Plugin Ecosystem: Access native device features through plugins - Rapid Development: Faster development cycle than native apps - Cost Effective: Single codebase for multiple platforms

Best Use Cases: - Content-driven apps - Business applications - Prototypes and MVPs - Apps with simple UI requirements - Cross-platform apps with limited native functionality needs

Considerations: - Performance may be slower than native apps - Limited access to latest platform features - UI may not feel completely native - Requires careful optimization for smooth performance

Cordova remains a viable option for many mobile app projects, especially when development speed, cost, and cross-platform compatibility are priorities over maximum performance and native platform integration.