23

How do I find out, which version my Cordova app is? I need that information to display it in an About screen.

Is it possible to read the config.xml and get the version string, which i am already maintaining in this file?

<widget ... version="0.0.1" ...

I would like to have a solution where I do not have to maintain the app's version number in several places in the code. Also, I do not want a plugin for that purpose, because most plugins do not support Android, iOS and browser as platforms.

Or am I overlooking a core plugin attribute?

Or is there a solution where I maintain one file, which is no the config.xml, and the config.xml gets the version information from that file?

How are you implementing "display version info" in your app's about screen? Any hint is appreciated.

My Cordova version is:

>cordova -v
4.2.0
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Strubbl
  • 709
  • 4
  • 15
  • 31
  • duplicate of http://stackoverflow.com/questions/23343902/plugin-to-get-the-version-of-phonegap-app – unobf Feb 03 '15 at 17:37
  • 3
    No, it is not a duplicate. I do not want this plugin solution, which @Kamal Reddy accepted. This is not working with browser as platform! – Strubbl Feb 04 '15 at 16:03

7 Answers7

24

If you are not interested in using any third party plugin to achieve this, then I feel Cordova hooks is the way to go. You can come up with a before_build hook to read the version from config.xml file and make use of the same in app.

Fortunately, this hook is readily available for you in the following link where they are reading version from config.xml and injecting it as a dependency wherever required.

Suggest you to also have a look at the official documentation of cordova hooks for better understanding.

In fact, you can also try out reading version info from config.xml and directly put it in some placeholder that you can create in your index.html to display the version info. This can be done before cordova build using hooks which in turn uses windows batch file that does the file operations. You can checkout a working sample of cordova hook that uses batch file in my github page.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Gandhi
  • 11,875
  • 4
  • 39
  • 63
  • 6
    +1 This I believe is a perfect answer. I'd prefer hooks over plugins for something like this as if Cordova changes I'm not dependant on waiting or forking a plugin and modifying it to work. If for whatever reason the hook stops working, the show still goes on. I'll give it 48 hours to see if there's any other answers, if not I will award you the bounty. Thanks – William Isted Mar 07 '17 at 15:02
  • If this meets the criteria of @strubbl's question then I ask that they accept this as the answer. – William Isted Mar 07 '17 at 15:05
  • 2
    A hook currently seems to be the best solution. Thanks for pointing it out. – Strubbl Mar 07 '17 at 16:18
  • @Strubbl glad it was helpful. Cheers – Gandhi Mar 07 '17 at 16:55
  • 1
    @william Isted thanks for the acknowledgement... Cheers – Gandhi Mar 07 '17 at 16:56
6

based on the other answers, i've created the following before_prepare hook which works best for me in cordova 9 / ionic 5.

it does not require any additional lib to parse the config xml as it gets the version by regex. it works in browser, iOS and android platform enviroment. also it does not work in ionic serve as cordova hooks are not exectued here.

#!/usr/bin/env node
// Add %%VERSION%% at any place to index.html file to be replaced by this script.

var fs = require('fs');
var path = require('path');

function fileStringReplace(filename, search, replace) {
    var content = fs.readFileSync(filename, 'utf8');
    content = content.replace(new RegExp(search, "g"), replace);
    
    fs.writeFileSync(filename, content, 'utf8');
}

module.exports = function(context) {
    var rawConfig = fs.readFileSync("config.xml", 'ascii');
    var match = /^<widget.+version="([\d\.]+)".+?>$/gm.exec(rawConfig);
    
    if(!match || match.length != 2)
        throw new Error("version parse failed");
    
    var version = match[1];

    fileStringReplace("www/index.html", "%%VERSION%%", version);
    console.log("replaced version to " + version);
}

add the following hook registration to config.xml:

<hook src="hooks/patch-indexhtml-version.js" type="before_prepare" />

index.html in action:

<html lang="de" data-appversion="%%VERSION%%">

now you can do something like this to obtain the version in your app:

document.documentElement.getAttribute("data-appversion");
cyptus
  • 3,346
  • 3
  • 31
  • 52
  • I also used 'before_prepare' to change the www/index.html file instead of platforms/~/index.html so all the platforms could have the same version at the same time. I tweaked a little bit of @cyptus' fileStringReplace(" part in hook.js. I changed so that www/index.html doesn’t have to have %%VERSION%% string always (data-appversion=“1.0.5” for ex. no need to run ng build -> cordova prepare every time for just a version change) – Minjeong Choi Aug 07 '20 at 03:57
  • ``var rootdir= context.opts.projectRoot; var fullfilename = path.join(rootdir, "www/index.html"); var versionRegex = /data-appversion="(.+?)"/; if (fs.existsSync(fullfilename)) { fileStringReplace(fullfilename, versionRegex,`data-appversion="${version}"`); }`` – Minjeong Choi Aug 07 '20 at 03:57
  • this does not work because this replaces the SOURCE file at `www/`. You don't want to replace at the source file, you want to replace at the file at `dest/`. Here how should be done: https://stackoverflow.com/a/65471679/1243247 – João Pimentel Ferreira Dec 28 '20 at 10:33
  • @JoãoPimentelFerreira in ionic `www/` is like `dest/` in your enviroment. still works fine with latest ionic. – cyptus Dec 28 '20 at 10:48
  • @cyptus ah, ok, in Apache Cordova `www/` is the source dir – João Pimentel Ferreira Dec 28 '20 at 11:10
5

Based on the script that @Gandhi pointed to in his answer, I updated the script to make it work for me with the current cordova@8: I use it as "after_prepare" hook, not "before_build", as in the later case the changed file got overwritten again when cordova copied the stuff to the platform directory soon after the hook has been executed ...

The script uses xml2js, so be sure to exec npm i xml2js --save-dev to make xml2js available.

#!/usr/bin/env node

// taken from https://www.bram.us/2015/01/04/cordova-build-hook-script-for-displaying-build-version-in-your-app/
// see https://stackoverflow.com/a/42650842
// This plugin replaces text in a file with the app version from config.xml.

// be sure to exec `npm i xml2js --save-dev` to make xml2js available


var wwwFileToReplace = "js/config.js";

var fs = require('fs');
var path = require('path');
var xml2js = require('xml2js');

function loadConfigXMLDoc(filePath) {
    var json = "";
    try {
        var fileData = fs.readFileSync(filePath, 'ascii');
        var parser = new xml2js.Parser();
        parser.parseString(fileData.substring(0, fileData.length), function (err, result) {
                           //console.log("config.xml as JSON", JSON.stringify(result, null, 2));
                           json = result;
                           });
        console.log("File '" + filePath + "' was successfully read.");
        return json;
    } catch (ex) {
        console.log(ex)
    }
}

function replace_string_in_file(filename, to_replace, replace_with) {
    var data = fs.readFileSync(filename, 'utf8');

    var result = data.replace(new RegExp(to_replace, "g"), replace_with);
    fs.writeFileSync(filename, result, 'utf8');
    //console.log("replaced in ", filename, "(", to_replace, " -> ", replace_with);
    var data2 = fs.readFileSync(filename, 'utf8');
    console.log(data2);
}

module.exports = function(context) {

    // var rootdir = process.argv[2]; // old cordova version
    var rootdir = context.opts.projectRoot;
    console.log("projectRoot=", rootdir);

    var configXMLPath = "config.xml";
    var rawJSON = loadConfigXMLDoc(configXMLPath);
    var version = rawJSON.widget.$.version;
    console.log("Version:", version);

    // var currentBuildPlatforms = process.env.CORDOVA_PLATFORMS.split(","); // old cordova version
    var currentBuildPlatforms = context.opts.cordova.platforms;
    //console.log(JSON.stringify(context));
    console.log("Current build platforms: ", currentBuildPlatforms);

    if (rootdir) {
        currentBuildPlatforms.forEach(function(val, index, array) {
            var wwwPath = "";
            switch(val) {
                case "ios":
                    wwwPath = "platforms/ios/www/";
                    break;
                case "android":
                    wwwPath = "platforms/android/assets/www/";
                    break;
                default:
                    console.log("Unknown build platform: " + val);
            }
            var fullfilename = path.join(rootdir, wwwPath + wwwFileToReplace);
            if (fs.existsSync(fullfilename)) {
                replace_string_in_file(fullfilename, "%%VERSION%%", version);
                console.log("Replaced version in file: " + fullfilename);
            }
        });
    }

}

Include this hook in your config.xml:

<hook src="scripts/after_prepare_read_app_version.js" type="after_prepare" />
Peter T.
  • 2,927
  • 5
  • 33
  • 40
3

i use the plugin describe at https://github.com/whiteoctober/cordova-plugin-app-version 1 step : cordova plugin add https://github.com/whiteoctober/cordova-plugin-app-version.git

2 step (cut and paste) If you are using jQuery or AngularJS, promise style is supported. Use something like:

cordova.getAppVersion.getVersionNumber().then(function (version) {
    $('.version').text(version);
});

If not, pass a callback function:

cordova.getAppVersion.getVersionNumber(function (version) {

alert(version);
});

cheers

alvaro562003
  • 678
  • 1
  • 6
  • 27
1

I would just do something like this:

cordova.getAppVersion.getVersionNumber().then(function (version) {
   if (!window.localStorage['version'] || window.localStorage['version'] <    version) {
       window.localStorage['version'] = version;
   }
});

That way you can call window.localStorage['version'] where ever you need it.

William Isted
  • 11,641
  • 4
  • 30
  • 45
  • 1
    Doesn't really explain how you get `cordova.getAppVersion` in the first place. – William Isted Mar 06 '17 at 13:33
  • 1
    @WilliamIsted cordova.getAppVersion again uses cordova plugin – Gandhi Mar 07 '17 at 08:09
  • it's a plugin that is deprecated as the project is kind of abandoned. it has problems with some devices, so I would not recommend it. https://github.com/whiteoctober/cordova-plugin-app-version – chesscov77 Jun 08 '17 at 04:25
1

I'm writing in December 2020. Now the simplest solution is to use the plugin cordova-plugin-app-version

Reads the version of your app from the target build settings [got from config.xml]

Install it

cordova plugin add cordova-plugin-app-version

Simply use something like this in your JS:

cordova.getAppVersion.getVersionNumber(function (version) {
  console.log('APP version is ' + version)
  $('.version').text(version)
})

In your html you may use this to insert the version wherever you'd like:

<span class="version"></span>
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
  • Not maintained anymore [Looking for maintainers](https://github.com/sampart/cordova-plugin-app-version/issues/100) – PiTheNumber Jul 26 '23 at 12:14
0

Here my compilation from different answers. I use it to update some parameters in Xcode project for plugins compilation.

You can see that I am getting here app id and name from config.xml

And you can add it to after_prepare hook:

<hook src="scripts/addBuildSettingsToXcode.js" type="after_prepare" />

#!/usr/bin/env node

let fs    = require('fs');
let xcode = require('xcode');
let path = require('path');
let et = require('elementtree');


module.exports = function (context) {
    //console.log(context);

    function addBuildPropertyToDebugAndRelease(prop, value) {
        console.log('Xcode Adding   ' + prop + '=' + value);
        myProj.addBuildProperty(prop, value, 'Debug');
        myProj.addBuildProperty(prop, value, 'Release');
    }

    function updateBuildPropertyToDebugAndRelease(prop, value) {
        console.log('Xcode Updating ' + prop + '=' + value );
        myProj.updateBuildProperty(prop, value, 'Debug');
        myProj.updateBuildProperty(prop, value, 'Release');
    }


    // Getting app id and name from config.xml
    let config_xml = path.join(context.opts.projectRoot, 'config.xml');
    let data = fs.readFileSync(config_xml).toString();
    let etree = et.parse(data);
    let appId = etree.getroot().attrib.id ;
    let appName = etree.getroot().find('name')['text'];

    // Building project path
    let projectPath = 'platforms/ios/' + appName + '.xcodeproj/project.pbxproj';

    // Opening Xcode project and parsing it
    myProj = xcode.project(projectPath);
    myProj = myProj.parseSync();

    // Common properties
    addBuildPropertyToDebugAndRelease('DEVELOPMENT_TEAM', 'CGXXXXXXX');
    addBuildPropertyToDebugAndRelease('CODE_SIGN_IDENTITY', '"Apple Development"');

    // Compilation properties
    addBuildPropertyToDebugAndRelease('ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES', 'YES');

    // Save project file
    fs.writeFileSync(projectPath, myProj.writeSync());

};

ArMouReR
  • 33
  • 7