How to read version (and other) information from Android and iOS apps using Java
How to read version (and other) information from Android and iOS apps using Java
Many times, especially when running automated tests against mobile apps, it is important to verify that the correct version of an app is used. This article describes easy ways to query the version information from both Android and iOS app packages using either command line tools or Java libraries. Both approaches can be easily integrated into a build pipeline.
The command-line way
For Android, to parse meta information from an APK file one approach is to use aapt like this:
aapt dump badging /path/test.apk
This prints a lot of information so we need to trim the output to what we are interested in:
aapt dump badging test.apk | grep -o 'versionName=[^,]*' | cut -d'=' -f 2 | cut -d ' ' -f 1 | tr -d "'"
Similarly, we can also query the version code (or any other information for that matter):
aapt dump badging /path/test.apk | grep -o ‘versionCode=[^,]*’ | cut -d’=’ -f 2 | cut -d ‘ ‘ -f 1 | tr -d "'"
If we have the app installed on a device (physical or simulator) which is connected via adb
adb shell dumpsys package package.of.the.app | grep versionName | cut -d'=' -f 2 | cut -d ' ' -f 1
adb shell dumpsys package package.of.the.app | grep versionCode | cut -d'=' -f 2 | cut -d ' ' -f 1
For iOS apps, let’s consider APP packages (used for simulators) first. If we are on Mac OSX, we can use defaults
(note that the path needs to be absolute):
defaults read /path/test.app/Info CFBundleShortVersionString
defaults read /path/test.app/Info CFBundleVersion
On Linux, we need plistutil
and an XML parser like xmllint
:
plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"
If we want to retrieve the version information from an IPA file (used for physical devices) we must unpack it first. Then, we can run the same commands as above:
# Unpack IPA file
unzip -q /path/test.ipa -d /tmp/ipa
# Mac OS
defaults read /tmp/ipa/Payload/test.app/Info CFBundleShortVersionString
defaults read /tmp/ipa/Payload/test.app/Info CFBundleVersion
# Linux
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"
# Clean-up
rm -rf /tmp/ipa
The Java way
One way to retrieve the version information from within a Java program is to use Runtime
or ProcessBuilder
to execute the above shell commands. However, this is platform-dependent and a bit brittle. It requires that the tools used (aapt
, plistutil
, xmllint
etc.) are available on the machine running the Java code.
Therefore, a native Java solution is preferable. Luckily, there are open source libraries for all our needs:
- apk-parser: This library allows us to read information from APK files.
- dd-plist: This library can process plist files used to store iOS app meta information.
- zip4j: This library is one (of many) options to handle zip files in Java.
Using Maven, we can include them in our project:
<properties>
<dd.plist.version>1.21</dd.plist.version>
<apk.parser.version>2.6.4</apk.parser.version>
<zip4j.version>1.3.2</zip4j.version>
</properties>
<dependencies>
<dependency>
<groupId>com.googlecode.plist</groupId>
<artifactId>dd-plist</artifactId>
<version>${dd.plist.version}</version>
</dependency>
<dependency>
<groupId>net.dongliu</groupId>
<artifactId>apk-parser</artifactId>
<version>${apk.parser.version}</version>
</dependency>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>${zip4j.version}</version>
</dependency>
</depdencies>
For Android, we can then simply fetch the version information like this:
String appPath = "/path/test.apk";
ApkFile apkFile = new ApkFile(new File(appPath));
ApkMeta apkMeta = apkFile.getApkMeta();
String versionName = apkMeta.getVersionName();
String versionCode = apkMeta.getVersionCode().toString();
For iOS, the code looks as follows for an APP package:
String appPath = "/path/test.app";
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(path);
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();
If we are dealing with an IPA file instead:
String appPath = "/path/test.ipa";
# Unpack IPA file
String tmpFolder = File.separator + "tmp" + File.separator + UUID.randomUUID().toString();
ZipFile zipFile = new ZipFile(appPath);
zipFile.extractAll(tmpFolder);
# Fetch version information
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(new File(new File(tmpFolder + File.separator + "Payload").listFiles()[0].getAbsolutePath() + File.separator + "Info.plist"));
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();
# Clean-up
Files.walk(Paths.get(tmpFolder)).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
For simplicity, the exception handling has been removed from the above examples.
The full code is available here.
All-in-one library
The demo above is part of the justtestlah
test framework. You can use the mobile-tools
JAR (which comes with minimum additional dependencies) to query version (and other meta) information from both Android and iOS apps. Import it as follows:
<properties>
<justtestlah.version>1.3.2</justtestlah.version>
</properties>
<dependencies>
<dependency>
<groupId>io.github.martinschneider</groupId>
<artifactId>justtestlah-mobile-tools</artifactId>
<version>${justtestlah.version}</version>
</dependency>
</depdencies>
Then, simply use it like this:
ApplicationInfo appInfo = new ApplicationInfoService().getAppInfo(appPath);
String versionName = appInfo.getVersionName();
String versionCode = appInfo.getVersionCode();
String applicationName = appInfo.getApplicationName();
appPath
can point to any APK, IPA or APP file.