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
- Getting Started
- Project Structure
- CLI Commands
- Platform Management
- Plugin System
- Configuration
- Device APIs
- UI Frameworks
- Development Workflow
- Building and Testing
- Debugging
- Performance Optimization
- Security
- Deployment
- Best Practices
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 = {
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'&': '&'
};
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.