-
Notifications
You must be signed in to change notification settings - Fork 127
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
Move iOS and Android Specific Code to Their Own Packages #49
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
--- | ||
title: Move iOS and Android Specific Code to Their Own Packages | ||
author: | ||
- Ian Meikle | ||
date: today | ||
--- | ||
|
||
# RFC0000: Move iOS and Android Specific Code to Their Own Packages | ||
|
||
## Summary | ||
|
||
Re-organize the React Native repo as a Yarn workspace monorepo, and move platform specific JS and native code for iOS and Android into their own packages. | ||
|
||
## Basic example | ||
|
||
Common APIs and components would remain in `react-native`: | ||
|
||
```js | ||
import {AppRegistry, Alert, View} from 'react-native'; | ||
``` | ||
|
||
Platform-specific components and APIs would be imported from the platform's module: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about things that should be cross platform but just haven’t been implemented on the other platform yet? Do they start in a platform specific package and move into the core package later? Or is it more based on the particular api/component in which case there would still be some things in core that don’t work on both platforms? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was thinking that an Like in the case of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the core should be slim enough that its pretty much required to have it all implemented. So for instance, checkbox wouldn't be part of core. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, would be good to keep core to a minimum "This is what you need to implement to be considered a full React Native implementation". Other stuff could be implemented by the platform implementers by submitting pull requests to the Slimmen'd repos (like what will probably happen with Windows' WebView implementation) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about a module that has 4 methods out of 5 that work on both platforms and wants to provide one, additional, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do think some kinds of revamp of if (Platform.OS === 'ios') {
if (typeof type !== 'undefined') {
console.warn('Alert.alert() with a 5th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.');
AlertIOS.alert(title, message, buttons, type);
return;
}
AlertIOS.alert(title, message, buttons);
} else if (Platform.OS === 'android') {
AlertAndroid.alert(title, message, buttons, options);
} else if (Platform.OS === 'windows') {
AlertWindows.alert(title, message, buttons, options);
} And because it depends on requiring There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing a suffix on the method is still bad because just because Android doesn't support it doesn't mean that Windows/MacOS/Ubuntu/other third party platforms won't. Rather than baking it into the method name it should just be documented which platforms it supports. |
||
|
||
```js | ||
// iOS | ||
import {Alert} from 'react-native-ios'; | ||
|
||
// Android | ||
import {Permissions} from 'react-native-android'; | ||
``` | ||
|
||
## Motivation | ||
|
||
React Native is for more than just iOS and Android - there are renderers for Windows, macOS, and even the DOM and even more on their way. | ||
|
||
This is analogous to when React [split into React and ReactDOM](https://reactjs.org/blog/2015/09/10/react-v0.14-rc1.html#two-packages-react-and-react-dom), and (in my opinion at least!) the separation of these modules was a benefit to the renderers of other platforms. | ||
|
||
Splitting iOS and Android out into their own packages would make them functionally like out-of-tree platforms. The process of doing this split should hopefully strengthen support for out-of-tree platforms as a result. | ||
|
||
The process of re-organizing these modules will, I hope, also result in more components being found that can be split out into their own repo and package as part of the slimmening. | ||
|
||
Platform-specific APIs could drop the platform suffix from their name. `PermissionsAndroid` could just be `Permissions` for example. | ||
|
||
React Native being a Yarn workspace might make it easier to make other modules that should be developed and released alongside the main project. It would be easier to make a more advanced RNTester app in-tree with its own set of dependencies, for example. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RNTester being its own package in-tree is unrelated and could happen anyways right? Perhaps that should be part of the RNTester proposal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if that could be done without re-arranging it into a monorepo first, Yarn requires the top level But to implement this idea, I was thinking I would first do a "smaller" pull request that just sets up a yarn workspace and nothing else, and that could get done sooner. Maybe the yarn workspace could be a separate proposal this one is dependant on? |
||
|
||
(**UPDATE**: I am working on a pull request to set up a monorepo, so other proposals could benefit from it regardless of this proposal's progress) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just fyi: This can probably not be done on GitHub and will have to be done at FB in one go. |
||
|
||
The IOS-only and Android-only files and folders at the top level of the repo could get organized into their respective packages. | ||
|
||
## Detailed design | ||
|
||
### Module exports: | ||
|
||
**Components** | ||
|
||
* `react-native` | ||
* **Components** | ||
* `AccessibilityInfo` | ||
* `ActivityIndicator` | ||
* `ART` | ||
* `Button` | ||
* `CheckBox` (I am unsure about this one. Android-only in main tree, but generic enough other platforms could implement it. Possible slimmening candidate?) | ||
* `FlatList` | ||
* `Image` | ||
* `ImageBackground` | ||
* `ImageEditor` | ||
* `ImageStore` | ||
* `KeyboardAvoidingView` | ||
* `ListView` (DEPRECATED. may be removed by the time of the split) | ||
* `Modal` | ||
* `Picker` | ||
* `SafeAreaView` | ||
* `ScrollView` | ||
* `SectionList` | ||
* `Slider` | ||
* `Switch` | ||
* `RefreshControl` | ||
* `StatusBar` | ||
* `SwipeableFlatList` | ||
* `SwipeableListView` (DEPRECATED) | ||
* `Text` | ||
* `TextInput` | ||
* `Touchable` | ||
* `TouchableHighlight` | ||
* `TouchableNativeFeedback` | ||
* `TouchableOpacity` | ||
* `TouchableWithoutFeedback` | ||
* `View` | ||
* `VirtualizedList` | ||
* `WebView` (DEPRECATED. [Moved to its own repo](https://github.com/react-native-community/react-native-webview)) | ||
* **APIs** | ||
* `Alert` | ||
* `Animated` | ||
* `AppRegistry` | ||
* `AppState` | ||
* `AsyncStorage` | ||
* `BackHandler` | ||
* `CameraRoll` | ||
* `Clipboard` | ||
* `DeviceInfo` | ||
* `Dimensions` | ||
* `Easing` | ||
* `findNodeHandle` | ||
* `I18nManager` | ||
* `InteractionManager` | ||
* `Keyboard` | ||
* `LayoutAnimation` | ||
* `Linking` | ||
* `NativeEventEmitter` | ||
* `NetInfo` | ||
* `PanResponder` | ||
* `PixelRatio` | ||
* `Settings` | ||
* `Share` | ||
* `StyleSheet` | ||
* `Systrace` | ||
* `TVEventHandler` | ||
* `UIManager` | ||
* `unstable_batchedUpdates` | ||
* `Vibration` | ||
* `YellowBox` | ||
* **Plugins** | ||
* `DeviceEventEmitter` | ||
* `NativeAppEventEmitter` | ||
* `NativeModules` | ||
* `Platform` | ||
* `processColor` | ||
* `requireNativeComponent` | ||
* `takeSnapshot` | ||
* **Deprecated PropTypes** | ||
* `ColorPropType` | ||
* `EdgeInsetsPropType` | ||
* `PointPropType` | ||
* `ViewPropTypes` | ||
|
||
* `react-native-ios` | ||
* **Components** | ||
* `DatePicker` (was `DatePickerIOS`) | ||
* `InputAccessoryView` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an example of something I could imagine being standard across many platforms. Also, should probably be pulled out with slimmening regardless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will move this to the main package when I revise this 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly I think this particular one should just get moved to a community rep. But that is a conversation for slimmening. Nothing to worry about for this proposal. |
||
* `MaskedView` (was `MaskedViewIOS`) | ||
* `Picker` (was `PickerIOS`) | ||
* `ProgressView` (was `ProgressViewIOS`) | ||
* `SegmentedControl` (was `SegmentedControlIOS`) | ||
* `SnapshotView` (was `SnapshotViewIOS`) | ||
* `TabBar` (was `TabBarIOS`) | ||
* **APIs** | ||
* `ActionSheet` (was `ActionSheetIOS`) | ||
* `Alert` (was `AlertIOS`) | ||
* `ImagePicker` (was `ImagePickerIOS`) | ||
* `PushNotification` (was `PushNotificationIOS`) | ||
* `StatusBar` (was `StatusBarIOS`) | ||
* `Vibration` (was `VibrationIOS`) | ||
|
||
* `react-native-android` | ||
* **Components** | ||
* `DrawerLayout` (was `DrawerLayoutAndroid`) | ||
* `ProgressBar` (was `ProgressBarAndroid`) | ||
* `Toast` (was `ToastAndroid`) | ||
* `Toolbar` (was `ToolbarAndroid`) | ||
* `ViewPager` (was `ViewPagerAndroid`) | ||
* **APIs** | ||
* `DatePicker` (was `DatePickerAndroid`) | ||
* `Permissions` (was `PermissionsAndroid`) | ||
* `TimePicker` (was `TimePickerAndroid`) | ||
|
||
|
||
### Hypothetical project overview (non-exhaustive) | ||
|
||
This is the *very basic* overview of what the GitHub repo could look like: | ||
|
||
``` | ||
-| project root | ||
--📁 {.circleci, .github, bots}/ | ||
--📁 flow/ | ||
--📁 flow-github/ | ||
--📁 packages/ | ||
---📁 react-native/ | ||
----📁 Libraries/ (All cross-platform JS) | ||
----📁 ReactCommon/ | ||
----📁 scripts/ | ||
-----📄 launchPackager.bat | ||
-----📄 launchPackager.command | ||
-----📄 packager.sh | ||
----📁 tools/ (contains bzl build-defs) | ||
---📁 react-native-ios/ | ||
----📁 Libraries/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related but perhaps another topic: I'd like to reconsider how the directories are named/organized in the RN project, which I've found a bit confusing and idiosyncratic for what most see as a "JS project". For example, the exported components and APIs are defined in a variety of places in the repo. (I tried to simplify the situation for contributors to React Native for Web by putting all the public exports in a flat directory) |
||
----📁 React/ | ||
----📁 scripts/ | ||
-----📄 ios-configure-glog.sh | ||
-----📄 ios-install-third-party.sh | ||
-----📄 react-native-xcode.sh | ||
----📁 third-party-podspecs/ | ||
---📁 react-native-android/ | ||
----📁 gradle/ | ||
----📁 keystores/ | ||
----📁 Libraries/ | ||
----📁 ReactAndroid/ | ||
---📁 react-native-git-upgrade/ | ||
--📁 RNTester/ | ||
--📁 scripts/ (CI, sync, etc. scripts) | ||
--📄 package.json (for the Yarn workspace) | ||
--📄 {readme, contributing, CoC, other markdown docs} | ||
``` | ||
|
||
I am not fully sure what the layout of the local-cli stuff will end up needing to be after this; with [this proposal](https://github.com/react-native-community/discussions-and-proposals/blob/master/proposals/0002-Extracted-Command-Line-Tools.md) the bulk of it will end up getting moved to another repo anyways. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Side note: when the cli is moved out it might be a good time to start working towards making this project packager agnostic, and pointing people to working packagers rather than including one in the project. This will surface issues related to the existing packager situation, like OSS RN packages being published without being compiled and being unable to run on pretty much any platform without first being passed through Babel using the RN babel config. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
IMO I kinda like them shipping un-compiled. Especially with JSI allowing for different JS engines to be used with React Native, it would be useful for it to be easy to use different babel configs for different engines (imo). Biggest thing would be opting in to using native async/await where available for a cleaner error stack trace Would be worth a discussion of its own for sure! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a difference between not compiling parts of the code that are effectively js standards but not available in older environments, and publishing code with early stage proposals |
||
|
||
ReactIOS and ReactAndroid would get entries in their `package.json` like this: | ||
|
||
```json | ||
{ | ||
"rnpm": { | ||
"haste": { | ||
"providesModuleNodeModules": ["react-native-ios"], | ||
"platforms": ["ios"] | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Platform-specific Flow prop types will probably need to stay bundled together in the main package. Ideally it could be set up so if you didn't have `react-native-ios` installed the iOS-only View props wouldn't show up when typechecking, for example. But I don't know if there's a way to do that with Flow yet. Would need [this issue](https://github.com/facebook/flow/issues/396) to be resolved, probably. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do other platforms like windows extend the props of components to provide platform specific props? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case of RNWindows they use the platform-overridable https://github.com/Microsoft/react-native-windows/blob/master/Libraries/Components/View/PlatformViewPropTypes.windows.js They don't seem to use platform overridable flow props anywhere. But a platform-specific |
||
|
||
The native build scripts (gradle, BUCK, xcode, etc.) will all need to be updated for the new code locations. Great care must be taken to not break the existing large ecosystem of native compnents on NPM! | ||
|
||
## Drawbacks | ||
|
||
- A lot of old open pull requests will need to be rebased if the codebase is re-arranged | ||
- The `react-native-ios` and `react-native-android` packages on NPM already have the following versions on them: | ||
> - `react-native-ios`: `1.0.0`, `1.0.1`, `1.1.0` | ||
> - `react-native-android`: `1.0.0`, `2.0.0`, `3.0.0` | ||
|
||
This will make React Native's future semver bump to `1.0.0` awkward, if not impossible. Unless it does what React did and jumps to version `60.0.0` or something like that. Or starts at version `4.0.0`, which seems kinda random. NPM also says that for various technical and security reasons, these versions can not be deleted and re-used. I've `npm deprecate`d them, which is the best I can do sadly. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively we use package names There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's definitely an option! That idiom would make it awkward for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Def. worth making another issue for, since your idea of having information about out-of-tree platforms in the main repo could probably be done independently of this proposal.
RNWeb and RNDom are different enough projects I think there's room for the both of them. But I really don't know about dropping the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about going babel’s route and going namespaced. @react-native/core it would allow for semver 1.0.0 and identify official packages ie react-native-web would immediately be identifiable as an external project. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We tried to reach out to the NPM user who owned that namespace, but had no luck in securing it. Would be a good option IMO if we could, however! 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry I deleted that message. I was mistaken in my search. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no worries! FWIW, facebook DOES own There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I couldn't see who owned If
Very straightforward. EDITED sorry I didn't quote them and tagged instead :P There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that (and @necolas' suggestion to use I've updated the proposal with both of these formats |
||
|
||
Using other names like: `@react/ios` and `react-ios` are also possibilities, since the React and React Native projects might be converging more in the future. | ||
- While community modules that want to cover multiple versions of RN could use the "bridge" versions of the `{ios,android}` modules to ease the transition there are packages that are not maintained that would get broken by a split like this. This is also an issue for Slimmened modules as well. | ||
- Even with a codemod to ease the transition, I don't imagine that transitioning codebases over will be something that is trivial. Maybe more than 2 versions should be given as a grace period before everything moves over? | ||
- Old documentation and tutorials will need to be updated. | ||
|
||
## Alternatives | ||
|
||
I believe that this is the best way forward for helping improve React Native's third party platform support, and also for helping improve the existing in-tree platforms by organizing platform-specific things into their own packages. If it's not, hopefully this will spawn discussions on better alternatives. | ||
|
||
## Adoption strategy | ||
|
||
There could be "bridge" versions of the `react-native-{ios,android}` modules for pre-split versions of React Native that simply import platform specific stuff from `react-native`. This would make it easier for people to make React Native addons that cover wider ranges of RN versions | ||
|
||
For a release or so, `react-native-{ios,android}` should probably be thin modules that just re-import stuff from `react-native` before the code actually gets moved over, to help people transition. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or the other way where React Native keeps exposing things from the native packages but the changes can be made in the native packages directly. |
||
|
||
For example, the import for `DatePickerIOS` in `react-native` would look like: | ||
|
||
```js | ||
get DatePickerIOS() { | ||
warnMoved('DatePickerIOS', 'react-native-ios', 'DatePicker'); | ||
return require('DatePickerIOS'); | ||
}, | ||
``` | ||
|
||
(where `warnMoved` is a hypothetical function warning once that an api is moved to another module) | ||
|
||
And `react-native-ios` would use Haste to import the module from `react-native`: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haste is one of those things that is a barrier to OSS contributors; we could probably do this the "standard" way instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that would definitely also make using other packagers (like parcel and webpack) less complex too. @gaearon removed haste from react a while ago and that could be looked to as an example of what could be done: Haste, however, happens to be really good at doing platform-specific overrides across multiple packages. A good replacement solution for this would need to be devised. Definitely a subject worthy of a proposal/discussion of its own probably! |
||
|
||
```js | ||
get DatePicker() { | ||
return require('DatePickerIOS'); | ||
}, | ||
``` | ||
|
||
Alternatively RNIOS and RNAndroid could be a `peerDependency` of React Native, and it would re-import the modules from the platform modules. | ||
|
||
Code-mods could be written to help with the transition. React and ReactDOM could be looked to as an example on how to do a transition like this as painlessly as possible. | ||
|
||
## How we teach this | ||
|
||
A documentation page laying out where all these components are getting moved around to should be made. This page could also document where Slimmen'd modules got moved to. | ||
|
||
## Unresolved questions | ||
|
||
* Should `react-native init` auto-install `{ios,android}` by default? (I lean towards yes) If so, should there be a command line switch to not install them if the user wishes? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How Windows and other platforms would provide their implementations of core components and APIs? Since imports are from
react-native
, not from platform-specific module, I guess that would be tricky and require certain platforms to export a minimal subset of APIs to be considered a valid platform for React Native?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Platforms can have an entry like this in their
package.json
which adds their platform to the haste search path:And if they wanted to override
ExampleComponent
inreact-native
,react-native-example
would haveExampleComponent.example.js
This solution depends on Haste, so if RN wants to drop Haste in the future a solution for platform overrides will need to be worked out.
Pretty much, or have a whole bunch of stub JS
UnimplementedView
files to address the Haste missing files errors