Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] How to publish custom modules for android. #2679

Closed
chirag04 opened this issue Sep 14, 2015 · 26 comments
Closed

[Android] How to publish custom modules for android. #2679

chirag04 opened this issue Sep 14, 2015 · 26 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@chirag04
Copy link
Contributor

Currently npm and cocopod is just for publishing custom mdules for ios. Let's document the best way for android also.

keeping this as a tracking issue for the known issue(http://facebook.github.io/react-native/docs/known-issues.html#content)

cc @brentvatne @ide @mkonicek

@foghina
Copy link
Contributor

foghina commented Sep 14, 2015

Right now you can publish native code to maven central / jcenter and JS code to npm, and simply ask users to add both to their apps. This is not ideal and in the future we might want to look at a unified dependency system. One such system that I can imagine is have developers still publish separately to npm / maven / cocoapods, but in their package.json specify the maven and cocoapods dependencies. So when a user adds that dependency to their npm project we pick it up and automatically add the maven and cocopods deps to android and iOS. WDYT?

@foghina
Copy link
Contributor

foghina commented Sep 14, 2015

That would also require us to write code that picks up new native modules / views from added dependencies. Right now you have to manually add those to your CatalystInstance on Android, not sure how it works on iOS, but I'm guessing it's similar.

@chirag04
Copy link
Contributor Author

I'm probably not the best person to answer this coz i don't have much experience with cocoapods/jcenter.

From past experience, npm worked really well for ios modules and then manually drag and drop to xcode project(one time task/not after every npm install).

@chirag04
Copy link
Contributor Author

Having to distribute on npm / jcenter / cocoapods would be really annoying. If we can leverage npm for android then i think we could use npm as the unified solution.

@ide
Copy link
Contributor

ide commented Sep 14, 2015

Currently I'm in favor of npm for distribution and cocoapods/gradle for linking.

@chirag04
Copy link
Contributor Author

@foghina @ide Since we can link to local jar files in build.gradle, how about using npm for publishing and then just linking the jar file in build.gradle. This means developer can ship the jar to npm.

Refer:
http://stackoverflow.com/questions/20700053/how-to-add-local-jar-file-dependency-to-build-gradle-file

@ide
Copy link
Contributor

ide commented Sep 14, 2015

I think there are two things we lose from the JAR:

  • It's more complex to publish code
  • It's harder to solicit PRs since people wanting to modify source now need to use a different setup

As a general engineering principle I'd rather things be fast and push caching as low as possible, where it makes sense. In this case if we could always build from source and tell gradle/javac to be smart when the lib source files haven't changed I think that'd be much better.

@chirag04
Copy link
Contributor Author

I see what you mean and def agree to it. I just published react-native-mail and tried to npm install and use it in a project.

Here is what i had to do:

  1. in android -> app -> build.gradle add compile project(':RNMail') as dependency.
  2. in android -> settings.gradle -> add RNMail like this:
rootProject.name = 'AwesomeProject'

include ':RNMail', ':app'

project(':RNMail').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-mail/android')

Seems like npm is a good option.

cc @ide @foghina

@skevy
Copy link
Contributor

skevy commented Sep 14, 2015

@ide @chirag04 plus 1 to distributing over NPM. I think it's been working relatively well for iOS packages...and @chirag04 if there really is only 3 lines to get something installed into an Android project (or at least 3 lines for simple packages), seems like we have a winner :)

I also think that linking external packages might be something that react-native-cli could help with down the road.

@brentvatne brentvatne changed the title [package][android] how to publish custom modules for android. [Android] How to publish custom modules for android. Sep 14, 2015
@corbt
Copy link
Contributor

corbt commented Oct 5, 2015

So I think it should be possible to use a custom gradle plugin to add files to the classpath. That plugin could then walk the node_modules tree and add any dependencies with an android directory to the classpath. It might even be possible to add them as separate projects, which should lead to them only being recompiled when a file has changed.

If that doesn't work, it should definitely be possible to create an autogenerated react-native.gradle configuration file that includes the configuration inputs @chirag04 mentioned above, that's maybe regenerated every time a user calls react-native run-android (or even potentially on an npm install hook).

I 100% do believe though that Android dependencies should be distributed through npm, and ideally as uncompiled code as @ide said. The barrier to entry for component maintainers is many times higher if they have to publish to both maven and npm, and there's also a lot of potential confusion for users if they need to keep their package.json and build.gradle in sync.

@chirag04
Copy link
Contributor Author

chirag04 commented Oct 5, 2015

@ide @foghina @skevy One quick problem with the npm approach is peerDependencies.

I'm not sure how to handle peerDependencies in build.gradle. One simply cannot leave out the react-native dependency in build.gradle unlike js modules.

@mkonicek
Copy link
Contributor

mkonicek commented Oct 5, 2015

Sorry for the delay on this. I agree that distributing Java sources via npm is the easiest way for contributors. People are already doing that and modules that contain Java code even have a little Android icon at React.parts.

Since we all agree, let's say this part is decided :)

Now we just need to figure out an easy way to consume the modules. I haven't had time to look into this yet but @chirag04's approach looks good. We could just include that in all projects generated by react-native init. This can be done here: https://github.com/facebook/react-native/blob/master/local-cli/generator-android/templates/src/app/build.gradle

@chirag04
Copy link
Contributor Author

chirag04 commented Oct 5, 2015

Nice. peerDependencies is probably the only concern left then. @mkonicek thoughts on that?

@ide
Copy link
Contributor

ide commented Oct 5, 2015

Could we check peer deps at runtime? I'm more concerned about subtle runtime bugs than code that doesn't compile because of mismatched versions. For example we could expose the RN release number via JS and Java and ObjC so module authors could check it.

@chirag04
Copy link
Contributor Author

chirag04 commented Oct 5, 2015

I did some search around and seems like there are ways to handle multi project dependencies which i guess what is peerDependencies here. I couldn't spend much time on it.

looping @satya164 who is active on android stuff. Would you have ideas?

@mkonicek
Copy link
Contributor

mkonicek commented Oct 5, 2015

I'm not familiar with peerDependencies yet. Will try to find time to look into it soon.

@chirag04
Copy link
Contributor Author

chirag04 commented Oct 6, 2015

@mkonicek Quick idea about peerDependencies: if the custom module and main project both depend on same lib, then let the custom module depend on the lib installed by main project.

eg:
RNMail(say) module depends on react-native. UIExplorer(say) depends RNMail, react-native both. Then let RNMail depends on a react-native version install by UIExplorer.

Also, It works in ios by giving two header search paths:

  1. RNMail(say) would look for react-native in node_modules directory inside RNMail
  2. RNMail(say) would look for react-native in node_modules directory in the parent(main) project.

@mkonicek
Copy link
Contributor

mkonicek commented Oct 6, 2015

OK I read a bit about the peerDependencies, let's check my understanding of what peerDependencies are for:

Let's say you want to write a module, called react-native-device-specs, and you want that module to depend on react-native, but not install a separate copy of react-native in node_modules. So, in your package.json you declare:

"version": "0.0.1",
"peerDependencies": {
  "react-native": "^0.11.4"
}

Now let's say react-device-specs also contains some Java code. To consume the module (and any 3rd party modules) you go to your build.gradle and do something like:

// Just an example
compile {
  source = ['src/main/java', '../node_modules']
}

Now, the Java code in node_modules has no dedicated way of declaring the version of the React Native Android binaries it is supposed to work with.

The module did, however, declare a dependency on ^0.11.4, which says the JS, Java, and Obj-C code are all designed to work with React Native ^0.11.4.

We release 0.11.4 Android binaries and 0.11.4 Obj-C sources whenever we release 0.11.4 to npm, all parts (JS, Obj-C, Java) of the framework are always defined by a single version number.

I believe this is how it works on iOS right now, correct? Can we do it the same way on Android?

@mkonicek
Copy link
Contributor

mkonicek commented Oct 6, 2015

An important question is: what do you do if you want your Obj-C or Java code have a dependency on some other Obj-C / Java library? Let's say you want to use wire in your module's Java code. Currently we have no good way of doing that. We can discuss this separately. I just talked to @tadeuzagallo and he says this hasn't been solved yet on iOS either.

One simple way of doing that on Android would be to simply publish wire-2.0.0.jar to npm along with your module code and tell Gradle to look for jars in node_modules.

@mkonicek
Copy link
Contributor

mkonicek commented Oct 6, 2015

A bit unrelated to the peerDependencies discussion but just talked to Tadeu about having a script (later down the road) to make installing cross-platform libraries easier.

Something like:

$ cd MyReactNativeApp
$ react-native link react-native-camera
All good to go, package.json, XCode and Gradle dependencies
for react-native-camera have been set up.

There was a related PR long time ago: #485

@chirag04
Copy link
Contributor Author

chirag04 commented Oct 6, 2015

@mkonicek link sounds awesome.

you got most of it with react-native-device-specs. Leaving js side of things aside and only java side here: All we need to achieve here is:

  1. Allow developers to compile react-native-device-specs from source in MyReactNativeApp.(ios compiles by default)
  2. Let react-native-device-specs not dictate the react-native version to use when using in a project like MyReactNativeApp.

For external deps like wire, it's ok to get them from jars just like how we get react-native now(maven, jcentral). They don't depend on react-native anyway. If they do, it should be an npm package.

@satya164
Copy link
Contributor

satya164 commented Oct 6, 2015

@chirag04 Sorry for the late reply. This thread is long and I didn't have enough time. I don't have any experince with distributing modules via maven/jcenter though.

I recently tried some Android modules from npm and they were straight forward to set up.

While distributing Java code through npm seems a good approach, I'm wondering, are the dependencies of those packages automatically downloaded by gradle? For external deps, I think, it can get cumbersome to add dependencies manually, and update them.

@mkonicek
Copy link
Contributor

mkonicek commented Oct 7, 2015

  1. Allow developers to compile react-native-device-specs from source in MyReactNativeApp.(ios compiles by default)

Yes, we should probably add something like this to new projects by default to allow that:

compile {
  source = ['src/main/java', '../node_modules']
}
  1. Let react-native-device-specs not dictate the react-native version to use when using in a project like MyReactNativeApp.

A 3rd party module, like react-native-device-specs must declare which version of RN it depends on, for both JS and Java, right? Otherwise if the Java code is designed to work e.g. with RN 0.13, it won't compile with 0.11 for example. Luckily, the module only needs to specify a single peerDependency on RN in its package.json which defines the version of all of JS, Java and Obj-C code.

When writing apps, people just need to be careful to upgrade React Native's both JS (in package.json) and Java code (in build.gradle) when upgrading to a newer React Native version. This could be a pain though ..

are the dependencies of those packages automatically downloaded by gradle?

Not yet but we should make it so, see point 1.

@mkonicek
Copy link
Contributor

mkonicek commented Oct 7, 2015

Created #3267 for a separate discussion on publishing React Native itself to JCenter vs npm.

@mkonicek
Copy link
Contributor

FYI I'm going to be available sporadically for the next 3 weeks (conference and vacation). Plan to pick this up and make publishing and consuming modules on Android a good experience once I'm back.

@mkonicek
Copy link
Contributor

Hey guys, I just looked at this issue again. It was super useful discussion. Some of what we talked about here inspired my work on "react-native link" and Mike Grabowski and Alexey Kureev implemented a much better version of it rnpm.

For creating new modules there is 'react-native new-library' which rnpm might improve further.

I'll close this now, thanks again for the input on this. I'm going to update the Known issues part since rnpm already solved it.

@facebook facebook locked as resolved and limited conversation to collaborators Jul 21, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 21, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests

8 participants