Order of Cordova Plugins Matters

July 28th 2017 Cordova iOS

I was convinced that it doesn't matter in what order plugins are added to a Cordova project. However, I recently encountered an issue with unexpected entries in iOS Info.plist file, which turned out to be caused by incorrect order of plugins in config.xml file.

I started out with cordova-plugin-geolocation plugin in my Cordova application:

cordova plugin add cordova-plugin-geolocation --save \
  --variable GEOLOCATION_USAGE_DESCRIPTION="My very own reason"

Using the plugin, the application can access the user location. On iOS I need to specify the reason, why the application requires access to location, which will be displayed to the user in the dialog when asking for permission. The plugin reads the corresponding text from a variable, which can be later changed in the config.xml file:

<plugin name="cordova-plugin-geolocation" spec="~2.4.3">
  <variable name="GEOLOCATION_USAGE_DESCRIPTION" value="My very own reason" />
</plugin>

With this configuration, the final Info.plist file (located in platforms/ios/<projectname>/<projectname>-Info.plist, where <projectname> is the name of the project) had the expected value for NSLocationWhenInUseUsageDescription from my variable:

<plist version="1.0">
  <dict>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>My very own reason</string>
  </dict>
</plist>

The problems started when I added cordova.plugins.diagnostic plugin to the same application:

cordova plugin add cordova.plugins.diagnostic --save

Suddenly, the value for NSLocationWhenInUseUsageDescription in Info.plist file changed:

<plist version="1.0">
  <dict>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>
      This app requires access to your location when the screen is on
      and the app is displayed.
    </string>
  </dict>
</plist>

I had to do a text search through my project folder to find the source of the changed text. I found it in the plugin.xml file:

<config-file target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
  <string>
    This app requires access to your location when the screen is on
    and the app is displayed.
  </string>
</config-file>

Obviously, the plugin contains a hardcoded value for NSLocationWhenInUseUsageDescription, hence now the value is defined twice in my project. Which value gets used? The answer can be found in ios.json file, which is placed in platforms/ios folder:

{
  "config_munge": {
    "files": {
      "*-Info.plist": {
        "parents": {
          "NSLocationWhenInUseUsageDescription": [
            {
              "xml": "<string>My very own reason</string>",
              "count": 1
            },
            {
              "xml": "<string>This app requires access to your location when the screen is on and the app is displayed.</string>",
              "count": 1
            }
          ]
        }
      }
    }
  }
}

This file serves as the basis for the final Info.plist file. The list of values for the same key will be applied one after the other, with the next one always overriding the previous one. As a result, the last value wins.

The order in which the values are listed in ios.json matches the order of plugins in config.xml. To fix my issue I need to make sure, that cordova-plugin-geolocation is the last one setting the key. I could move it to the end by removing and re-adding it to the project:

cordova plugin remove cordova-plugin-geolocation --save
cordova plugin add cordova-plugin-geolocation --save \
  --variable GEOLOCATION_USAGE_DESCRIPTION="My very own reason"

Alternatively, I could just change the order in config.xml manually, but then I need to delete the platforms and plugins folders and reinstall everything based on the contents of config.xml:

cordova prepare

Now, the order of values for NSLocationWhenInUseUsageDescription in ios.json is correct:

{
  "config_munge": {
    "files": {
      "*-Info.plist": {
        "parents": {
          "NSLocationWhenInUseUsageDescription": [
            {
              "xml": "<string>This app requires access to your location when the screen is on and the app is displayed.</string>",
              "count": 1
            },
            {
              "xml": "<string>My very own reason</string>",
              "count": 1
            }
          ]
        }
      }
    }
  }
}

And so is the value in the final Info.plist file:

<plist version="1.0">
  <dict>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>My very own reason</string>
  </dict>
</plist>

Another word of caution: make sure that on the build server you start with a clean working folder and reinstall all the plugins for every build, otherwise the changes to config.xml won't affect the Info.plist file because it is generated when the plugins are installed, not when the project is built.

Copyright
Creative Commons License