Handling dependencies conflict and locking in React Native
UPDATE 08/2018 : This article is deprecated, you can now handle this very easily with native dependency locking with Gradle >= 4.8.
Developing React Native apps is fast and enjoyable because we rarely have to deal with the native part of the application thanks to all the native modules available. Sadly, sometimes we encounter issues very close to the iOS or Android native development side. Recently, we came across a module conflict and locking issue on Android.
Section intitulée the-problemThe problem
The app we were working on is dependent on many native modules and specifically these two:
🗂 /app/android/app/build.gradle
dependencies {
...
compile project(':react-native-firebase-analytics')
compile project(':react-native-onesignal')
Which gives us, after trying to build… 💥
:app:transformClassesWithJarMergingForDebug FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:transformClassesWithJarMergingForDebug'.
> com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: com/google/android/gms/common/api/internal/zzcc.class
After some googling and browsing some stackoverflow issues, it seems that this is linked to a conflicting library, sadly not much more information is available about where to look.
Gradle has a nice command to display the dependency tree that might help us.
$ ./gradlew app:dependencies
+--- project :react-native-firebase-analytics
| +--- com.facebook.react:react-native:+ -> 0.48.4 (*)
| +--- com.google.firebase:firebase-core:+ -> 11.4.0
| | \--- com.google.firebase:firebase-analytics:11.4.0
| | +--- com.google.firebase:firebase-common:11.4.0
| | | +--- com.google.android.gms:play-services-basement:11.4.0
...
| \--- com.google.firebase:firebase-analytics:+ -> 11.4.0 (*)
+--- project :react-native-onesignal
| +--- com.facebook.react:react-native:+ -> 0.48.4 (*)
| +--- com.onesignal:OneSignal:3.+ -> 3.6.5
| +--- com.google.android.gms:play-services-gcm:+ -> 11.0.2
| | +--- com.google.android.gms:play-services-base:11.0.2
| | | +--- com.google.android.gms:play-services-basement:11.0.2 (*)
We can see that both react-native-firebase-analytics
and react-native-onesignal
depends on com.google.android.gms:play-services-basement
but with different versions.
Section intitulée the-solutionThe solution
Via Gradle, we can exclude the conflicting module com.google.android.gm
from both our dependencies like so and then ask for a specific version. This also prevents including too many libs from Google play services in our APK (less bundled code, yeah!).
dependencies {
compile(project(':react-native-firebase-analytics')) {
exclude group: 'com.google.android.gms'
}
compile(project(':react-native-onesignal')) {
exclude group: 'com.google.android.gms'
}
compile 'com.google.android.gms:play-services-gcm:11.4.0'
compile 'com.google.android.gms:play-services-basement:11.4.0'
Section intitulée locking-android-dependenciesLocking Android dependencies
After solving the conflict and trying to compile again a few weeks later, we got the same issue back without any native code modification of our app! 💥
This time the Gradle command showed us this:
+--- project :react-native-onesignal
| +--- com.facebook.react:react-native:+ -> 0.48.4 (*)
| \--- com.onesignal:OneSignal:3.+ -> 3.6.5
+--- project :react-native-firebase-analytics
| +--- com.facebook.react:react-native:+ -> 0.48.4 (*)
| +--- com.google.firebase:firebase-core:+ -> 11.6.0
| | \--- com.google.firebase:firebase-analytics:11.6.0
| | +--- com.google.firebase:firebase-common:11.6.0
| | | +--- com.google.android.gms:play-services-basement:11.6.0
+--- com.google.android.gms:play-services-basement:11.4.0 -> 11.6.0 (*)
react-native-onesignal
still got the dependency excluded but com.google.firebase:firebase-core
now requires the 11.6.0
version. Since the dependency is not explicitly locked by the module, gradle resolves it with the latest version available.
Changing these two lines to require the latest version fixed the issue, but just until the next release of the firebase module…
compile 'com.google.android.gms:play-services-gcm:11.4.0'
compile 'com.google.android.gms:play-services-basement:11.4.0'
On a JavaScript project, we usually go with yarn or npm to manage dependencies. Thankfully, since yarn came out, we got a nice yarn.lock file to lock our dependencies and the npm equivalent package-lock.json. Gradle has no built-in system for this but Netflix solved it with a nice plugin.
To use this plugin, add this at the top of your android/app/build.gradle
:
apply plugin: 'nebula.dependency-lock'
and this to android/build.gradle
:
dependencies {
classpath 'com.netflix.nebula:gradle-dependency-lock-plugin:4.+'
then the command cd android && ./gradlew generateLock saveLock
will generate a nice android/app/dependencies.lock
that you can add to git to be sure to always have the same dependencies on every installation.
Native knowledges are always nice to have while working on a React Native project, those tips will improve the stability of your project with strict reproducible builds. We are glad to only had this problem on Android since it seems way more complicated on iOS.
Section intitulée resourcesResources
Commentaires et discussions
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnelles !
React Native
Développez des applications mobiles natives et cross-platform pour iOS et Android
Ces clients ont profité de notre expertise
Discourse est un logiciel libre pour forum de discussions très puissant, sur lequel Mix with the Masters s’appuie pour créer et animer sa communauté. Nous avons appliqué une forte customisation du logiciel sur plusieurs aspects : thème graphique complet en accord avec la charte graphique du site ; plugin dédiés pour afficher un paywall ; implémentation…
JoliCode accompagne l’équipe technique Dayuse dans l’optimisation des performances de sa plateforme. Nous sommes intervenus sur différents sujets : La fonctionnalité de recherche d’hôtels, en remplaçant MongoDB et Algolia par Redis et Elasticsearch. La mise en place d’un workflow de réservation, la migration d’un site en Twig vers une SPA à base de…
Dans le cadre du renouveau de sa stratégie digitale, Orpi France a fait appel à JoliCode afin de diriger la refonte du site Web orpi.com et l’intégration de nombreux nouveaux services. Pour effectuer cette migration, nous nous sommes appuyés sur une architecture en microservices à l’aide de PHP, Symfony, RabbitMQ, Elasticsearch et Docker.