Cordova: All flavors must now belong to a named flavor dimension.

Messing around with Cordova I stumbled upon the following exception when building the app for Android:

A problem occurred configuring root project 'android'.
> All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html

The almighty folks over at Stackoverflow suggest to update the build.gradle and add a flavorDimensions parameter into the android section. As this smells like a dirty workaround for an issue that should (as always) not be happening in the first place, I found some deeper digging into this topic from Dave Alden. To sum it up: cordova-android@6.4.0 requires Gradle v4, but cordova-plugin-crosswalk-webview is incompatible with Gradle 4.

This leaves you with 3,5 options:

  • Upgrade cordova-android to 7+: This might mess up all your other plugins as they did some breaking changes (that’s why I still had to stick to 6.4.0). Quite a lot of Cordova plugins are not yet compatible with Cordova v7+ as of today.
  • Downgrade cordova-android even further: This has the risk of some other side-effects, missing stability and security updates, etc.
  • Downgrade Gradle to v3: This might lead to incompatibilities with e.g. cordova-plugin-crosswalk-webview
  • Abandon Cordova altogether and switch to React Native, Xamarin or Weex… but yeah.

Further references

How to build you own Open GApps package

The Open GApps project provides a convenient way to get up-to-date Google App packages (most often used in combination with custom ROMs). Unfortunately they do not always offer the most recent versions and it takes some time until new Android releases are reflected on the official Open GApps project website. As of this writing, Android 8.1 is the most recent Android version and is not yet in the portfolio of the Open GApps project.

Fortunately they publish their automation and all necessary assets on GitHub. Therefore building your package from their sources is pretty feasible. The following guide is done for macOS, but should be quite similar to most Linux distributions (hint: you can use the beevelop/android Docker image to save some time):

# Install lzip via brew (Open GApps depends on it)
brew install lzip
# Clone the main repository
git clone git@github.com:opengapps/opengapps.git
# Download the sources for your targeted architecture (arm64 in my case)
# Downloading and „uncompacting“ the repositories takes quite some time
# Get a coffee or two in the meantime
./download_sources.sh --shallow arm64

# The final step is building the package itself
# This also does take quite some time
# especially depending on your CPU power (due to compression stuff, etc.)
make arm64-27-stock

The following script might help you getting started by using the Docker image mentioned above. Just run the following commands inside the Docker container (e.g. docker run -it --rm beevelop/android):

apt install build-essential lzip git zip
# SSH-Key on the machine is required and has to be added to your GitHub account
git clone git@github.com:opengapps/opengapps.git
./download_sources.sh --shallow arm64
make arm64-27-stock
# The command should great you with:
# SUCCESS: Built Open GApps variation stock with API 27 level for arm64 as [...path...]

Afterwards you can transfer the resulting zip file from Docker container to your host machine using docker cp:

docker cp practical_wilson:/root/opengapps/out/open_gapps-arm64-8.1-stock-20180203-UNOFFICIAL.zip .
# from there on scp it to your local computer and put it on your gorgeous mobile phone

fastlane supply: Google Api Error (Google Play)

fastlane is an awesome tool to release your iOS and Android apps. It handles all your tedious tasks, like generating screenshots, dealing with code signing, and releasing your application. From my experience fastlane is pretty reliable and a true blessing when developing mobile applications. supply is the component of fastlane that is responsible for updating Android apps (binaries), release management (e.g. beta & alpha tracks) and the respective metadata (store listing and screenshots) on the Google Play Store.

Using supply to automate the alpha releases, I had to deal with the following error message:

[10:04:54]: Updating track 'alpha'...
[10:04:55]: Uploading all changes to Google Play...

[!] Google Api Error: multiApkShadowedActiveApk: Version 2100384 of this app can not be downloaded by any devices as they will all receive APKs with higher version codes.

Not only does this complication keep the builds failing, it also prevents releasing new alpha builds – therefore effectively jamming the release cycle. According to a related GitHub Issues the error gets triggered after promoting a release directly from alpha to production (skipping the beta track). As of this writing there is a pretty fresh (14 hours old) Pull Request, which tries to circumvent the outlined problem. Until the possible fix is released, you can workaround the problem by manually uploading a new APK to the alpha track. All subsequent builds should be fixed.


  • The described problem could be reproduced with supply 2.19.0 on Ubuntu 16.04.

Error Logging for Ionic 2 with Sentry (Raven.js)

Sentry is a realtime, platform-agnostic error logging and aggregation platform, that helps me catch errors across different projects. Raven.js is Sentry’s official browser JavaScript client and supports „quite a few“ technologies and frameworks (like plain-old JavaScript, Node.js: Express, Koa, Connect, Angular.js 1, Angular, Ember, React, Vue,…).

Ionic 2 comes with an excellent Error-Handling by default. It simply smashes all exceptions right into the developer’s face (the „React approach“ to handle things). This in itself is alright for development, but in production you should probably log all exceptions with solutions like Sentry.

To log exceptions with Sentry on Ionic 2, I decided to extend the existing IonicErrorHandler to additionally forward the logs to Sentry. The snippet below gives an example:

import * as Raven from 'raven-js';
import {NgModule, ErrorHandler} from '@angular/core';
import {IonicApp, IonicModule, IonicErrorHandler} from 'ionic-angular';
import {MyApp} from './app.component';
import {FoobarPage} from '../pages/home/home';

Raven
  .config('*YOUR DSN*')
  .install();

class RavenErrorHandler extends IonicErrorHandler implements ErrorHandler {
  handleError(err: any): void {
    super.handleError(err)
    Raven.captureException(err.originalError);
  }
}

@NgModule({
  declarations: [MyApp, FoobarPage],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [MyApp, FoobarPage],
  providers: [{provide: ErrorHandler, useClass: RavenErrorHandler}]
})
export class AppModule {}

ERROR ITMS-90071: CodeResources file must be a symbolic link

Using a continuous integration solution to efficiently develop iOS applications, I stumble upon the following issue when trying to upload an .ipa file using Apple’s Application Loader (Version 3.0)

ERROR ITMS-90071: “This bundle is invalid. The CodeResources file must be a symbolic link to _CodeSignature/CodeResources. Make certain that the bundle is on a locally-mounted volume [not a remote SMB volume], and be certain to use the Mac OS X Finder to compress it.”

The error might be caused by the way your CI solution creates your resulting archive or just due to running the build process from a command line instead of old-fashioned XCode with GUI.

To fix this issue you need to manually change the structure of your application file.

# extract your application archive file (ends with .ipa)
unzip Foobar.ipa -d tmp/

# change to the application folder (ends with .app)
cd tmp/Payload/Foobar.app

# overwrite or create the mentioned symbolic link
ln -fs _CodeSignature/CodeResources CodeResources

# change to the tmp/ folder
cd ../..

# recreate the application archive file (.ipa)
zip -yr9 Foobar.ipa Payload/

You should then be able to successfully upload your iOS application with Apple’s gorgeous Application Loader.

You can also use the copy’n’pastable example below (resulting file is called Fixed.ipa).

unzip *.ipa -d tmp/
cd tmp/Payload/*.app
ln -fs _CodeSignature/CodeResources CodeResources
cd ../..
zip -yr9 Fixed.ipa Payload/
cd ..
mv tmp/Fixed.ipa .
rm -r tmp
ls -lah

ADB over network with Android 2.3

For testing purposes I use an old Android 2.3 device that runs an old version of Cyanogenmod. Unfortunately I could not find an option to enable ADB over network in the device’s settings. I am not a fan of plugging in the device every time I would like to debug an application. But, fortunately, there is a way to enable ADB over network via Terminal if your device is rooted:

su
setprop service.adb.tcp.port 5555
stop adbd
start adbd

Afterwards you can connect as usual by running adb connect device-ip on your development machine.

To disable ADB over network you can set the TCP port option to -1:

setprop service.adb.tcp.port -1
stop adbd
start adbd

Cordova 5: Building signed Android applications

Even though signing Android applications is quite simpler than signing iOS applications, it’s sometimes annoying to set it up and reliably automate it (without using Phonegap or comparable services). Recently I tried automating singing a Cordova application for Android and struggled finding a reliable documentation.

The official Cordova documentation states, that you can easily append a bunch of arguments to the build command to automatically sign your Android application:

Keystore (--keystore): Path to a binary file which can hold a set of keys.
Keystore password (--storePassword): Password to the keystore
Alias (--alias): The id specifying the private key used for singing.
Password (--password): Password for the private key specified.
Type of the keystore (--keystoreType): pkcs12, jks (Default: auto-detect based on file extension)

The catch is, you need to append these arguments as platformopts (POPTS):

cordova build [PROD] [TARGET] [EXP] [PLATS] [BUILDCONFIG] [-- POPTS]

  PROD:   --debug|--release
  TARGET: --device|--emulator|--target=FOO
  EXP:    --experimental [EXPERIMENTALFLAGS]
  PLATS:  PLATFORM [...]
  BUILDCONFIG: --buildConfig=CONFIGFILE
  POPTS:  platformopts

Therefore the command to build a signed Cordova android application becomes:

cordova build --release android -- --keystore=my.keystore --storePassword=K3ySt0reP4ssw0rd --alias=foobar --password=K3yP4ssw0rd

Alternatively, you could specify them in a build configuration file (build.json) using (–buildConfig) argument.

{
     "android": {
         "release": {
             "keystore": "my.keystore",
             "storePassword": "K3ySt0reP4ssw0rd",
             "alias": "foobar",
             "password" : "K3yP4ssw0rd",
             "keystoreType": ""
         }
     }
 }

and respectively

cordova build --release android --buildConfig=buildConfig.json

I tested all those commands with Cordova 5.4.0