-
Notifications
You must be signed in to change notification settings - Fork 129
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
Native Modules #96
Comments
I’ve been working on these problems in my own tooling in iOS (as evinced by my
This is a hodgepodge, but it solves my problem that podspecs otherwise "go rogue" when I try to make better devex with integrated pod support for dependencies. I concur that standardizing on pods would be ideal. And that it would be a significantly breaking change. I don't think that's such a big cost - upgrading RN is a challenge every month or so today, so making a hard thing a little harder for more people - once - to get a smoother experience on the other side seems like a reasonable price. I in the absence/in advance of a shift to all-pod all the time, I propose that one of the best things we could do is allow switching on |
A few things I would love to see 🙌🏼:
Currently it's barely documented, and it's hard to understand which subspecs are necessary. target 'ProjectName' do
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', # Include this for RN >= 0.47 -> include it in React/Core?
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43 -> same?
'RCTAnimation', # Needed for FlatList and animations running on native UI thread
'RCTNetwork',
'RCTText',
'RCTWebSocket', # Needed for debugging
# Which subspecs can be added? Which are absolutely necessary? Which can be excluded?
# https://github.com/facebook/react-native/blob/master/React.podspec
]
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga' # include it in React/Core?
# Why should I install this? I need context
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
end
subDependencies = {
// you can override sub dependencies versions
"com.google.android.gms:play-services-base": "16.0.1",
"com.google.android.gms:play-services-maps": "16.0.0",
"com.google.maps.android:android-maps-utils": "0.5+",
} And in libraries, something like: dependencies {
// dependency: (packageName: string, defaultVersion: string) => string
compileOnly dependency("com.facebook.react:react-native", "+")
implementation dependency("com.google.android.gms:play-services-base", "16.0.1")
implementation dependency("com.google.android.gms:play-services-maps", "16.0.0")
implementation dependency("com.google.maps.android:android-maps-utils", "0.5+")
} Not sure if it's doable, no idea on how to tackle the same issue on iOS. |
Hey @zoontek, thank you for your comment!
The intent was to make it integrated with all projects by default. Adding it later would be complex, as you pointed out.
This is something @axe-fb was asking about (how do we deal with multiple libraries depending on a different version of another library). @axe-fb, do you think |
Thinking about @zoontek point, maybe the app-level Podfile should be generated by RN - every build (where a build might be a single run of |
I think the best way to resolve this question is that CocoaPods also works with the default Ruby that comes pre-installed with macOS (and macOS is required to develop iOS apps with React Native), so you don't even need to install Ruby separately, if you don't want to. |
Definitely would be great to see more guidance around how native modules should package up any third-party SDKs they depend on. I've seen all sorts, some where frameworks are bundled into the static library, but you typically need to embed them into the application anyway. Others where you manually provide the SDK yourself at the application level (which seems to work better IMO). Better tooling for creating native modules would be a great win. |
Some thoughts on how we migrate the community and support legacy link: I think the tool should continue supporting legacy linking with a flag like —useLegacyLink. If you try to link a package that hasn’t migrated to the new system without using the flag it should print a warning to the console saying that the package isn’t following the latest standard, asking the developer to open an issue on that project asking it to support the new system, and then telling them to rerun with —useLegacyLink. |
I think it shouldn't be scoped to just native modules. This is about native dependencies (native modules, viewmanagers, future Fabric/TurboModule dependencies, etc).
I'd like to make sure that the standard includes specifications for how TurboModule/Fabric components should be built/packaged. A lot of this is still work in progress, but it's important to plan ahead for the future direction of native dependencies. A few things to consider:
Folks build C++ heavy integration may have opinions in this space. cc @matthargett, @acoates-ms |
Some thoughts on legacy link support IMHO, we can still have a proposal without breaking the way existing modules are linked. Linking is only a pain when react-native modules depend on other native sdks. If that is not the case, always forcing gradle or cocoapods may result in the native module author having to publish 2 packages - one on npm, one of gradle or cocoapods locations. |
Some thoughts on upgrading Another issue with the current linking story is that it modifies the XCode project or MainApplication.java during This may also be inline with how we could codegen for Fabric/TurboModules. |
@axemclion I support that approach 100%. |
Thank you @axemclion for comments. Here are my thoughts:
How that could be the case? I assume Gradle and CocoaPods would be always part of the library, regardless it depends on native dependencies at the time of its creation or not. This makes it easy to add native dependencies in the future and removes the pain of integrating CocoaPods later. We would use it not just for handling native dependencies, but also for exporting public interface of the native module (header files and so on). Right now, we have to "find" where "Name.xcodeproj" is located, having a Podfile would eliminate that brittle step. I would imagine CocoaPods to be consumed from "node_modules" - "npm" should be the source of truth for consuming React Native libraries.
I am not sure if this is the case on iOS, where support is added via build phase. It is not "codified" into the application. On Android, we modify
Isn't this what "react-native link" basically would do, with a difference that you'd fire it yourself rather than at a build time? It's an interesting goal and I would love to get there at some point, but I think we need to make sure we have all the smaller building blocks ready and in place. Right now, abstracting this away from end-users would mean we need to support most of their use-cases as it's no longer possible to work with CocoaPods spec and/or Gradle to adjust the configuration. |
Thank you @fkgozali again for providing a decent feedback with a view of the upcoming re-architecture.
Right! It should be about any React Native library that contains native code, whether via dependency or a source code. I am going to update the title to reflect that.
True. Even if we don't specify it right now (because it's work in progress), I think it would be great to make sure that the tools and choices we make do not make it a deal breaker in the future. I think before we move on with this issue, I would like to understand how we can consume C++ modules this way. I guess it's something we can discuss it in facebook/react-native#22989.
Is there a decent support for iOS in Buck? Last time I checked, I remember it was the reason why React Native was using it on Android only. I think this is a decision we should make before we continue - if we decide on using Buck to handle dependencies instead of CocoaPods/Gradle - I'd rather discuss it now and save the community from migrating one more time in the future. |
Overall, I feel like I overused the term "define standard" in the original issue. My intent was not to provide end-to-end description for writing native modules (with handling conflicts, distribution and various additional things related to building native code), but standardise on the tools we use for that. I still pursue decent tooling for automating work with native modules, but I think it's too ambitious to be talking about the specifics of compilation for each of the platforms when we don't have a stable way to link the dependency and generate a library. For instance, claim that Gradle / CocoaPods are "blessed" choices that are supported by our tooling and let the developers adjust the configuration of each of them themselves, to their needs. Do not describe how to add a native dependency, rely on Gradle / CocoaPods documentation and community for answering that question. I see it as enabling the community of iOS and Android developers to be able to use the tools they know and are proven to work. I think we just need to make sure that certain things are possible with Gradle / CocoaPods and if not, think if there are any alternatives (such as Buck) that are worth giving a go. Once the choice is made, describe "minimum" configuration required and add support via "link". Note: This is only breaking for iOS, but we would leave old |
I think one thing that would be interesting to lay out is to actually put together what it would look like to build a module in a few months from now. Like, I wanna build a new native module using TurboModules to expose some functionality to RN. What is that gonna look like? I think answering this question with a concrete structure will help us iterate on something. |
I'm glad we're all on the same page about how dependency management for react-native should just work! The major struggle of the dependency management story for myself is that developers are often forced to use whichever option the library maintainers used (e.g. manual link or link through pods) - and with many different libraries doing this differently, it becomes very hard to manage. For example, many libraries allow you to link through your Podfile like so:
But then also specify other "options" which require changing the other things you bring in:
In this case, it would be much easier to work with if the configuration looked like a function:
(Obviously, this is probably a limitation of CocoaPods, but it would be nice, wouldn't it?) Obviously, the library itself should be in charge of maintaining its own dependencies - which it is - but in this sort of example, we are also in charge of handling the Another great example of this issue is with I was working on updating dependencies again tonight (including the latest react-native update), and one challenge that I face is that react has to be linked both through Pods and manually in the Xcode Project. For example, unless you move all of your 3rd party dependencies to Pods, you have to keep the React project in Xcode. Otherwise the manually linked projects can't find various React things that they need. But if React isn't in Pods, then the libraries linked through Pods can't find it either. So you have to do both, but then you'll usually get a build error related to having multiple linked versions of react, so you tack on a hacky script to the end of the Podfile to make sure that none of the individual pods point to react:
I also wonder if this could be an opportunity to improve iOS build time. Currently, our iOS app takes 40 minutes to archive. Exporting our Android app with the same dependencies and react-native code takes about 12 minutes. I feel as if there must be a way to utilize dependency management better so that it doesn't have to re-compile all of these third party files which stay the same every time. I'm not sure, but that may be a limitation of CocoaPods. I'm excited to see how this discussion evolves; it's definitely a sorely needed improvement! |
The way I got around the react native linkings/finding issue is by keeping react native only in pods and exposing the headers to the main project. This is at the end of the pod file.
|
Nice! Whatever direction this goes, I think React Native should ultimately just be linked one way. The docs recommend that you link through Pods if you're adding it to an existing project, and you're required to link through Pods if you are going to add 3rd party RN dependencies via Pods. But |
I think that no matter which way it goes, there should be an option to easily use the different package managers / link native dependencies manually. Speaking of iOS, not everything can be deployed as a CocoaPod - sometimes 3rd party dependency providers allow you to download Defining CocoaPods as a recommended way sounds great since it's probably the easiest way, let's just make sure that we leave some freedom with the choice. |
As far as I'm aware, you can use |
This is just a "recommendation". It doesn't change anything in the React Native itself or the way it exposes its modules to the outside world. It only updates React Native CLI to:
If you don't want to use CocoaPods, you just opt-out from using "react-native link" and must be aware that there might be some issues while working with React Native CLI. We will actively work on fixing them anyway, but there's no guarantee they work the first time you run it. |
I think most of the questions and unknowns here are around (i) supporting TurboModules in the future and (ii) the size and scope of the proposal. I would suggest I submit a PR (draft of a proposal) that describes how this recommendation could be implemented right now, for existing modules. It doesn't require much work from my side and I feel like it would help us move the discussion forward. Then, we leave a "TBD" section for "new architecture of React Native" and we connect it with the ongoing work on TurboModules and CocoaPods support for them. We will discuss the details in the PR and on Discord and once TurboModules section is finished (or at least, we know that there are no blockers and we can specify the details later), we will merge it and start implementing the details in the CLI itself. PS. If I missed any questions or important topics to address before going forward, please comment! |
Update: @fson has been working on making CocoaPods the default in the project template -> facebook/react-native#23563 This is a first step towards implementing this issue. I am tracking the work required on the CLI side here: react-native-community/cli#183 |
I dislike that |
@punksta this is exactly the new approach we are going to implement as a part of this issue. You can track our work here: react-native-community/cli#254 Its still a heavy WIP and others team members are yet to send their iOS/Android parts. But it's a preview. Will update everyone here with the estimates once we have them. |
Work in CLI is moving on. We have finished part of it, there's now two PRs left. You can track progress in #288, which is an umbrella issue for it. |
I think we are good to close this one for now. I'm super excited about autolinking, it's going to change how people use and perceive native modules entirely. Let's start a new thread in the future once TurboModules and Fabric are closer to shipping in open source. |
@cpojer, to be honest, I still consider this issue something that should be
implemented. Link was just one of the things I highlighted, I also wanted
to focus a bit more on debugging experience and creating libraries.
I can keep working on this inside CLI, but wanted this issue to be a note
for others that there's work happening in that space (in case someone wants
to work on that in the future in paralel).
How we can facilitate that?
…On Wed, 3 Apr 2019 at 17:43, Christoph Nakazawa ***@***.***> wrote:
Closed #96
<#96>
.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#96 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACWcxiZt5QM9XHTxvyuNq0DZG1Tmlmolks5vdMwqgaJpZM4aWlJ_>
.
|
Do you have a list of actionable things that we need to do right now? |
I don't. But I'll be back to this issue once auto-linking is done and then, I will try to outline the action items. |
@grabbou Thoughts on this now that autolinking is done? Thanks! |
I've been thinking about the state of native modules and what we could do better to improve them. After working with few other React Native team members on the details, I think I am ready to share what I've came up with so far.
This issue is based on some feedback I gathered from #60. Since the scope is a bit broader than just linking libraries, I decided to open a new issue.
This document describes the current developer experience working with native modules, most important problems and areas where we could improve. The intent is for this issue to become umbrella for various RFCs that will be submitted as a part of it.
Glossary of terms
I want this issue to be super easy to understand, hence I decided to break down the terminology that I use throughout this issue. If you find anything unclear, please comment and I'll add it here.
react-native
- React Native CLIreact-native link
- command to link a dependency to your project. It is a part of React Native CLIProblem
Working with native modules in React Native can be overwhelming. From the library author perspective, it is not clear how native dependencies could be handled and what is the recommended structure for a module.
From the app developer perspective, it is not easy to consume React Native dependencies from the npm registry. Some of them are pure Javascript libraries that can be installed just like any other Javascript dependency. Others require additional steps to be performed (namely Android and iOS files to be added to respective iOS and Android projects). This can be confusing and increases the entry-barrier for the developers that want to extend basic capabilities of React Native with 3rd party modules.
See the reasons highlighted below for details.
Lack of standard
There is no standard or recommended way to create a React Native library. This problem is especially visible on iOS, where no built-in package manager for native dependencies is available (see "No support for native dependencies on iOS" section).
As a result, each React Native library tends to have a slightly different structure in order to satisfy their use-case. This makes it hard to provide a decent tooling support that can automate most of the repeated steps (see "Poor tooling support" section).
It is also not clear at a glance what steps need to be performed. This degrades the developer experience from a trivial “yarn add” to many complex instructions.
No support for native dependencies on iOS
Some React Native dependencies rely on native libraries in order to provide their functionality. It is a common practice to wrap a native SDK and expose it to React Native developers.
On Android, the open-source community settled on Gradle as the go-to package manager. However, iOS doesn’t have a built-in package manager that would handle downloading, compiling and linking native dependencies to a project. As a result, React Native developers are often required to perform a set of manual steps, some of them, requiring a decent native knowledge.
Few approaches to handling native dependencies have developed over the course of few years within the community, some of them are listed below:
There's been a great discussion in #60 on how this could be addressed.
Poor tooling support
Since there is no recommended or the de facto standard way to create a React Native library, there are no commands that would support developers in making them. That contributes to a poor debugging experience (discussed in the next section).
There are few community driven initiatives, such as react-native-create-library (which is a perfect example of repeating format from a different module, in this case - react-native-share).
At the same time, on iOS, “react-native link” is unable to offer the level of reliability and performance that React Native developers would expect. Since there’s no standard way of managing React Native dependencies, it has to apply few heuristics in order to work (for example, locate Android and iOS source files using glob).
Debugging
The debugging experience while working with native modules could be improved. In order to test their libraries, developers need to create an example React Native app where they can require their library from source and make sure it meets their expectations.
This can be set up in many ways, each having a different developer experience.
For example, one of the most popular ways of setting up a demo project for the library is to run a “react-native init” command to create a stand-alone React Native app and make the library a dependency.
This is usually done using file protocol (symlinks are not supported) that tells a package manager to copy files from the specified location and move them over to node_modules, as if they were installed from the registry. You can see it live in react-native-fbads.
The above approach has the following drawbacks:
changes to the library are not respected in real-time - developers have to re-install dependencies for
Solution - Define the standard
We should describe how to create a React Native library and build reliable tooling around that. This should be done via series of RFCs.
Managing native dependencies
The standard should describe how to create a React Native library that has native code and native dependencies and how it can be exported for the end user in the easiest way possible.
While doing so, we should focus on maximising the strengths and capabilities of existing package managers for the given platform, such as Gradle and CocoaPods in order to reduce the overall maintenance cost and support of additional use-cases. Going this direction would make the specification really small in terms of the rules and make it familiar to those who have experience working with iOS and Android already.
One advantage is that Expo is also already using CocoaPods and Gradle to support "ejected" applications, which might be an additional driver for promoting this pattern throughout the community. That means we can share the effort to support the same set of tools for both Expo and React Native developers. All in all, Expo developers interact with React Native CLI once they eject from Expo.
Complementary tooling for scaffolding libraries
Creating packages for React Native’s been always pretty complex and manual process that can potentially lead to annoying issues down the road. It would be great to extend the capabilities of the “react-native” command line tools to support that use-case.
The suggested approach would be to provide support for:
See Flutter documentation for reference on how this can be addressed.
Better debugging experience
The standard project structure should provide great developer experience out of the box. That said, library generated with the
react-native
(see Improved Tooling section) should have an example app that avoids the drawbacks mentioned in the Debugging section.A good example of such project structure is
react-native-opentok
that doesn’t copy library sources to example app’s node_modules, making the changes done to the library being reflected in the realtime.Another one is
react-native-camera
that uses custom Metro configuration to require the library from source.Whole-new linking experience
The purpose of the
react-native link
would be to automate adding a React Native library to a React Native project. It would only work for libraries that follow the standard library structure. That means, linking a library that has "Gradle" and "CocoaPods" inside.Having a clear set of supported use-cases would make it much easier to make it reliable and reduce the overall cost of its maintanance. The current
react-native link
has been working in more-or-less its original form for around 2 years. That's a lot given the amount of updates that did happen to React Native.The intent is for React Native developers to be able to run
react-native install XYZ
without worrying about the details of a particular library. With CocoaPods and Gradle, it wouldn't require much of the effort (especially compared with the currentlink
maintanance) to offer such improvement.Additional platforms
This proposal would be only targeted at Android and iOS (that are part of React Native right now). It would be great to work with Windows team on specifying the details for their platform. Right now, they can extend
react-native link
(and other CLI commands) on themselves. I would expect them to provide support for their standard using this mechanism too.Next steps
I would appreciate your feedback on this document and encourage a discussion what we could do better in order to make this experience better for React Native developers.
The next step would be to submit an RFC with the standard structure for a library, how it should manage dependencies, how the example app should be provided for best debugging experience.
Rollout
Once the RFC gets merged, we will work inside
react-native-cli
to implement it. This will require some additional tools and commands to be created (they will be described in the RFC).It is worth noting that React Native itself, as a framework, will remain neutral as far as the project structure and how the dependencies should be handled. The term "standard" is probably overused here. I decided to open this issue here, instead of
react-native-cli
repository for better visibility.The text was updated successfully, but these errors were encountered: