From 0b7d9a256b196fef03ceba805c0d813967d088a0 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Tue, 11 Feb 2020 14:33:19 +0100 Subject: [PATCH 1/3] React Native Test App as a Package --- ...0000-React-Native-Test-App-as-a-Package.md | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 proposals/0000-React-Native-Test-App-as-a-Package.md diff --git a/proposals/0000-React-Native-Test-App-as-a-Package.md b/proposals/0000-React-Native-Test-App-as-a-Package.md new file mode 100644 index 00000000..6a8db640 --- /dev/null +++ b/proposals/0000-React-Native-Test-App-as-a-Package.md @@ -0,0 +1,285 @@ +--- +title: React Native Test App as a Package +author: + - Tommy Nguyen +date: February 2020 +--- + +# RFC0000: React Native Test App as a Package + +## Summary + +Provide a React Native test app for all platforms as a package. + +A working prototype of this can be found here: +https://github.com/microsoft/react-native-test-app + +## Motivation + +Maintaining and keeping React Native up to date with the latest version can be +time consuming. Things can break if you're not careful, even with +[React Native upgrade helper](https://react-native-community.github.io/upgrade-helper/). +Not all developers have much experience in native development on a single +platform, let alone _all_ platforms they want to support. Adding support for +another platform, such as Windows or macOS, can be overwhelming. + +Consider the following scenarios: + +- As maintainers of a community package in our spare time, we don't have the + capacity to create and maintain test apps. + + - Our inner development loop consists of using `npx react-native init`, and + testing things manually. + - Alternatively, we have set up CI for iOS and Android, and would love to add + support for Windows and macOS but lack the resources. + +- We have a monorepo with multiple separate experiences that all need test apps. + It is a pain having to upgrade all of the test apps individually with every + release. Our upgrade process can sometimes take weeks to perform. + +- As a consumer of React Native, we should try out release candidates to find + issues early and contribute back, but due to one of the scenarios above, there + is simply too much friction. + +The proposed package will, at minimum, provide a customizable test app for all +the major platforms that React Native currently runs on, i.e. iOS, Android, +Windows, and macOS. The ultimate goal of this package is to make adding support +for a platform as simple as `npm add --save-dev`, and upgrading to latest React +Native is just a version bump. + +We expect the package will: + +- Help developers set up a consistent native environment for both local + development and CI agents +- Reduce or eliminate the friction to upgrade React Native, and try out release + candidates +- Reduce or eliminate the overhead of adding support for new platforms + +With common test apps, we will also be able to provide additional value for +everyone simultanously. For instance: + +- We will handle upgrading of the test apps to support the latest mobile SDK +- We can provide a common infrastructure for native testing +- We can provide optional modules for commonly used services, such as auth or + App Center integration + +## Detailed design + +Given an existing React Native app/library, we imagine the ideal flow for +**initial integration** with the native test apps should be: + +1. Create folder/package for the test app to live in. + - This package should have a dependency on the app/library to be tested, and + on the test app package. +2. Create a manifest to declare entry points. Optionally, it should also be + possible to declare initial props, native source files and resources. + - This is a **one-time cost**, and should be stable across versions. + - If there is a single entry point, the app may boot directly into this app. + - Otherwise, if there are multiple entry points, we will show them in a list. + - It should still be possible to have the app boot into one of these via + deep linking. + - Native dependencies should be taken care of by auto-linking behind the + scenes. +3. Run `npm install` inside this folder as one would usually do. +4. Depending on the desired target platforms, the next steps are: + - Android: + - Make minimal changes to `build.gradle` (one-time cost) + - Open the project in Android Studio + - iOS: + - Make minimal changes to `Podfile` (one-time cost) + - Run `pod install` + - Open the newly generated Xcode workspace + - Windows: + - <@afoxman or @acoates-ms to fill in> + - macOS: + - See steps for iOS + +A hypothetical package should end up looking something like below: + +``` +my-awesome-component +├── example +│ ├── App.js +│ ├── build.gradle (new/modified) +│ ├── package.json +│ ├── Podfile (new/modified) +│ └── yarn.lock +├── my-awesome-component.podspec +├── my-awesome-component-dev.podspec (new, optional) +├── package.json +├── src +└── yarn.lock +``` + +For a hypothetical monorepo: + +``` +my-monorepo +├── package.json +├── packages +│ ├── my-awesome-component-android +│ │ ├── build.gradle (new/modified) +│ │ ├── package.json +│ │ └── src +│ ├── my-awesome-component-core +│ │ ├── package.json +│ │ └── ... +│ └── my-awesome-component-ios +│ ├── my-awesome-component.podspec +│ ├── my-awesome-component-dev.podspec (new, optional) +│ ├── package.json +│ ├── Podfile (new/modified) +│ └── src +└── yarn.lock +``` + +Platform specific project files should no longer be checked in. They are either +provided by React Native Test App package or are generated at build time. + +Once the test app package is configured, **adding an additional platform** +should only require doing one of the platform specific substeps of step 4. + +**Upgrading React Native** to the latest version, or any desired version, should +only require bumping the version of this package. + +This package should not require additional tools other than the ones required by +React Native itself, and should not fundamentally change how a package is +structured. + +### The Manifest + +The manifest will be the file where we declare all our entry points, and +resources that should be included. + +We haven't decided what this manifest looks like. Our initial thought is to use +React Native CLI's +[configuration format](https://github.com/react-native-community/cli/blob/master/docs/configuration.md) +as a starting point. This is what +[auto-linking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#how-does-it-work) +uses today, and we can extend it to support our scenarios. + +For iOS (and macOS), we will need to extend the format to include additional +native source files and assets. An Xcode project generated today, will include a +main app target, and two test targets: One for "normal" tests, and one for UI +tests. We will need to be able to generate `.podspec` files that can support +all. + +Entry points will also be declared in the manifest. Entry points tell the test +app how to launch your app. For instance, given the declaration below: + +```js +module.exports = { + ... + "entryPoints": [ + { + "name": "MyFeature", + "entryPoint": "MyFeatureTestScreen", + "initialProps": { + "showStatus": true + } + }, + { + "name": "MySecondFeature", + "entryPoint": "MySecondFeatureTestScreen", + "presentationStyle": "popover" + } + ] +} +``` + +The mobile test app could look like this: + +``` +┌──────────────────────┐ +│ │ +│RN Test App │ +├──────────────────────┤ +│ MyFeature >│ +│ ─────────────────────┤ +│ MySecondFeature >│ +├──────────────────────┤ +│ │ +│ │ +│ │ +│ │ +│╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲│ +``` + +Tapping on one of these cells will launch the corresponding app. If defined, +`initialProps` will be passed to the appropriate method call, and +`presentationStyle` will tell the test app how to present the new view. + +One could also forgo the name, in which case the `entryPoint` string will be +used instead. If only one entry point is declared, the test app should launch +directly into that app. + +All of this should be transparent to the consumer; they should only have to deal +with the manifest. + +## Drawbacks + +- Package maintainers no longer have full control of the test app +- Bugs in the test app may take longer to get fixed +- Limitations in existing tools can cause confusions, e.g. files are not + added/removed until `pod install`/`update` is run + +## Alternatives + +### Electrode + +[Electrode Native](https://www.electrode.io/) is a platform by Walmart Labs +that provides an opinionated, batteries-included solution for integrating React +Native components into existing apps. It includes everything you'll ever need +such as React Native initialization code, code generators, code push, facilities +to test individual components etc. + +From a cursory glance, it seems that Electrode decides which React Native +version will be embedded, and which bundler to use. Whereas one of the main +goals of React Native Test App is making it simpler to switch between React +Native versions, and it doesn't care whether Metro or Haul is used. Because +Electrode is all-inclusive, it seems to be overkill for single component +packages. You just want a test app for development and run tests on CI. + +### Expo + +[Expo](https://github.com/facebook/react-native/tree/master/RNTester) is a great +solution to get started, and you don't mind that it's including a lot of things +that your app won't use. Once your app matures to a point where it needs better +control over what's included, or it needs to consume native modules that aren't +bundled in the SDK. Ejecting brings you back to the world of having to maintain +React Native yourself. + +Expo also isn't aimed at core contributors/maintainers. Its main goal is to +simplify getting started with your React Native app. If you're looking to +implement a native module of your own, you'll need to eject and stay ejected. +This defeats the purpose of using Expo in the first place. + +### React Native CLI + +[This tool](https://github.com/react-native-community/cli) creates a new project +with a specifiable React Native version. It can be further customized with +templates. We can see this test app package being part of the default template, +with a single entry point defined. + +### RNTester + +[RNTester](https://github.com/facebook/react-native/tree/master/RNTester) is an +app that showcases React Native views and modules. It is currently tightly +coupled to React Native and requires building the framework with the app. + +## Adoption strategy + +- Get buy-in from the community; we want to make sure that this package aligns + with what package maintainers expect of a test app +- Submit the first few PRs to volunteering react-native-community packages to + show how it can be integrated + +## How we teach this + +- Point to real PRs showing how it can be integrated +- Aim to become part of the default create-react-native-app template + +## Unresolved questions + +- Manifest format +- Versioning scheme From 1643b370a227633611d8479a916f9b7311783baa Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Thu, 20 Feb 2020 14:51:48 +0100 Subject: [PATCH 2/3] Addressed some comments --- ...0000-React-Native-Test-App-as-a-Package.md | 56 +++++++++++++++---- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/proposals/0000-React-Native-Test-App-as-a-Package.md b/proposals/0000-React-Native-Test-App-as-a-Package.md index 6a8db640..cceedb19 100644 --- a/proposals/0000-React-Native-Test-App-as-a-Package.md +++ b/proposals/0000-React-Native-Test-App-as-a-Package.md @@ -170,6 +170,9 @@ app how to launch your app. For instance, given the declaration below: ```js module.exports = { ... + "assets": [ + ... + ], "entryPoints": [ { "name": "MyFeature", @@ -213,6 +216,11 @@ One could also forgo the name, in which case the `entryPoint` string will be used instead. If only one entry point is declared, the test app should launch directly into that app. +Alternatively, we should be able to omit the `entryPoints` block and retrieve +the list of entry points from +[AppRegistry](https://github.com/facebook/react-native/blob/master/Libraries/ReactNative/AppRegistry.js#L165) +instead. + All of this should be transparent to the consumer; they should only have to deal with the manifest. @@ -242,17 +250,28 @@ packages. You just want a test app for development and run tests on CI. ### Expo -[Expo](https://github.com/facebook/react-native/tree/master/RNTester) is a great -solution to get started, and you don't mind that it's including a lot of things -that your app won't use. Once your app matures to a point where it needs better -control over what's included, or it needs to consume native modules that aren't -bundled in the SDK. Ejecting brings you back to the world of having to maintain -React Native yourself. - -Expo also isn't aimed at core contributors/maintainers. Its main goal is to -simplify getting started with your React Native app. If you're looking to -implement a native module of your own, you'll need to eject and stay ejected. -This defeats the purpose of using Expo in the first place. +[Expo](https://expo.io/) provides an app called Expo client that includes +multiple versions of the runtime in a single binary and makes it easy to switch +between them. Each version of the runtime uses a single version React Native. + +[Snack](https://snack.expo.io/) is an in-browser editor like +[CodeSandbox](https://codesandbox.io/) for React Native apps. You can choose the +version of Expo SDK to target using a drop-down menu. A specific version of the +SDK usually maps to a distinct React Native version, e.g. SDK 36 uses React +Native 0.61 and SDK 35 uses React Native 0.59. + +This is the quickest and easiest way to test React Native libraries for iOS, +Android, and web, provided that they depend on primitives in React Native core +or the most common dependencies in the ecosystem like +`react-native-gesture-handler`, `react-native-reanimated`, `react-native-maps`, +`react-native-svg`, and so on. + +There are two reasons why this isn't feasible for the particular use case +described in this proposal. First, if a library includes its own native +dependencies, it cannot be run inside of Expo client unless the appropriate +native code is also included in the client at build-time. Second, Expo client +apps are not yet built for Windows and macOS. You would not be able to test on +those platforms. ### React Native CLI @@ -277,9 +296,22 @@ coupled to React Native and requires building the framework with the app. ## How we teach this - Point to real PRs showing how it can be integrated -- Aim to become part of the default create-react-native-app template +- Make it simple to get started with, e.g.: + - A library template for + [react-native-cli](https://github.com/react-native-community/cli) so + developers can run + `react-native init ProjectName --template "react-native-library-template"` + to generate a project with test apps for all platforms ready to go + - Ship as default with library project generators such as + [Bob](https://github.com/react-native-community/bob) or + [create-react-native-module](https://github.com/brodybits/create-react-native-module) ## Unresolved questions - Manifest format - Versioning scheme + +## Related discussions + +- https://github.com/react-native-community/discussions-and-proposals/issues/96 +- https://github.com/facebook/react-native/issues/6292 From 76919be199af87bdafccf6956fe2b919e1f02d20 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Mon, 27 Jul 2020 22:54:47 +0200 Subject: [PATCH 3/3] React Native Test App should support multiple versions --- proposals/0000-React-Native-Test-App-as-a-Package.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-React-Native-Test-App-as-a-Package.md b/proposals/0000-React-Native-Test-App-as-a-Package.md index cceedb19..c1d37988 100644 --- a/proposals/0000-React-Native-Test-App-as-a-Package.md +++ b/proposals/0000-React-Native-Test-App-as-a-Package.md @@ -140,7 +140,8 @@ Once the test app package is configured, **adding an additional platform** should only require doing one of the platform specific substeps of step 4. **Upgrading React Native** to the latest version, or any desired version, should -only require bumping the version of this package. +only require bumping the version of `react-native`. The test app should support +multiple versions of React Native, including being able to compile from source. This package should not require additional tools other than the ones required by React Native itself, and should not fundamentally change how a package is @@ -309,7 +310,6 @@ coupled to React Native and requires building the framework with the app. ## Unresolved questions - Manifest format -- Versioning scheme ## Related discussions