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
Nous avons également accompagné l’équipe d’Evaneos sur le framework javascript AngularJS permettant d’industrialiser la création d’application web moderne & ergonomique. Cela a garanti une bonne architecture initiale afin d’assurer la pérennité et la maintenabilité des applications. L’intervention de JoliCode a permis de mettre en place une architecture…
L’équipe d’Alain Afflelou a choisi JoliCode comme référent technique pour le développement de son nouveau site internet. Ce site web-to-store incarne l’image premium de l’enseigne, met en valeur les collections et offre aux clients de nouvelles expériences et fonctionnalités telles que l’e-réservation, le store locator, le click & collect et l’essayage…
Groupama Épargne Salariale digitalise son expérience client en leur permettant d’effectuer leurs versements d’épargne salariale en ligne. L’application offre aux entreprises une interface web claire et dynamique, composé d’un tunnel de versement complet : import des salariés via fichier Excel, rappel des contrats souscrits et des plans disponibles, …