From b13b49fd32d317b6889586492cc1fe0240414cf1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 12 Aug 2025 16:05:29 -0600 Subject: [PATCH 1/4] Move existing docs to a new page --- README.md | 11 ++---- .../philosophies/CROSS-PLATFORM.md | 35 +++++++++++++++++++ contributingGuides/philosophies/INDEX.md | 1 + 3 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 contributingGuides/philosophies/CROSS-PLATFORM.md diff --git a/README.md b/README.md index 26997b95e4f1..e3a73ed08253 100644 --- a/README.md +++ b/README.md @@ -443,15 +443,7 @@ Files should be named after the component/function/constants they export, respec - All React components should be PascalCase (a.k.a. UpperCamelCase 🐫). ## Platform-Specific File Extensions -In most cases, the code written for this repo should be platform-independent. In such cases, each module should have a single file, `index.js`, which defines the module's exports. There are, however, some cases in which a feature is intrinsically tied to the underlying platform. In such cases, the following file extensions can be used to export platform-specific code from a module: -- Mobile => `index.native.js` -- iOS Native App/Android Native App => `index.ios.js`/`index.android.js` -- Web => `index.website.js` -- Desktop => `index.desktop.js` - -**Note:** `index.js` should be the default and only platform-specific implementations should be done in their respective files. i.e: If you have mobile-specific implementation in `index.native.js`, then the desktop/web implementation can be contained in a shared `index.js`. - -`index.ios.js` and `index.android.js` are used when the app is running natively on respective platforms. These files are not used when users access the app through mobile browsers, but `index.website.js` is used instead. `index.native.js` are for both iOS and Android native apps. `index.native.js` should not be included in the same module as `index.ios.js` or `index.android.js`. +This section has moved [here](contributingGuides/philosophies/CROSS-PLATFORM.md). ## API building When adding new API commands (and preferably when starting using a new one that was not yet used in this codebase) always @@ -659,6 +651,7 @@ This application is built with the following principles. 5. UI updates with data from the server 1. **Cross Platform 99.9999%** +This section has moved [here](contributingGuides/philosophies/CROSS-PLATFORM.md). 1. A feature isn't done until it works on all platforms. Accordingly, don't even bother writing a platform-specific code block because you're just going to need to undo it. 1. If the reason you can't write cross-platform code is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths. 1. If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing if (platform=iOS) { }, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic. diff --git a/contributingGuides/philosophies/CROSS-PLATFORM.md b/contributingGuides/philosophies/CROSS-PLATFORM.md new file mode 100644 index 000000000000..a9b78d527085 --- /dev/null +++ b/contributingGuides/philosophies/CROSS-PLATFORM.md @@ -0,0 +1,35 @@ +# Cross Platform Philosophy +Learn how the app supports features across all our different platforms. + +Currently supported platforms: +- Web +- Mobile Web +- Desktop +- iOS +- Android + +The goal is: **Cross Platform 99.9999%** + +## Rules +### - Features MUST be implemented on all supported platforms +A feature isn't done until it works on all platforms. Any platform-specific code blocks will be asked to be undone. + +### - ReactNative bugs MUST be fixed upstream +If the reason cross-platform code cannot be written is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths. + +### - Features that don't exist on all platforms MUST use NOOP shims +If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing `if (platform=iOS) { }`, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic. + +### - Platform code MUST be placed in dedicated files and folders +Put all platform specific code in dedicated files and folders (see below) and reject any PR that attempts to put platform-specific code anywhere else. This maintains a strict separation between business logic and platform code. + +## Platform-Specific File Extensions +In most cases, the code written for this repo should be platform-independent. In such cases, each module should have a single file, `index.js`, which defines the module's exports. There are, however, some cases in which a feature is intrinsically tied to the underlying platform. In such cases, the following file extensions can be used to export platform-specific code from a module: +- Mobile => `index.native.js` +- iOS Native App/Android Native App => `index.ios.js`/`index.android.js` +- Web => `index.website.js` +- Desktop => `index.desktop.js` + +**Note:** `index.js` should be the default and only platform-specific implementations should be done in their respective files. i.e: If you have mobile-specific implementation in `index.native.js`, then the desktop/web implementation can be contained in a shared `index.js`. + +`index.ios.js` and `index.android.js` are used when the app is running natively on respective platforms. These files are not used when users access the app through mobile browsers, but `index.website.js` is used instead. `index.native.js` are for both iOS and Android native apps. `index.native.js` should not be included in the same module as `index.ios.js` or `index.android.js`. diff --git a/contributingGuides/philosophies/INDEX.md b/contributingGuides/philosophies/INDEX.md index b992d1665f61..05ed9ed750ba 100644 --- a/contributingGuides/philosophies/INDEX.md +++ b/contributingGuides/philosophies/INDEX.md @@ -7,3 +7,4 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ## Contents * [Offline Philosophy](contributingGuides/philosophies/OFFLINE.md) * [Routing Philosophy](contributingGuides/philosophies/ROUTING.md) +* [Cross-Platform Philosophy](contributingGuides/philosophies/CROSS-PLATFORM.md) From 39d18a5efb410208dbf3623d721394db7cc203ba Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 12 Aug 2025 16:14:23 -0600 Subject: [PATCH 2/4] Add stuff about mobile web --- contributingGuides/philosophies/CROSS-PLATFORM.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/contributingGuides/philosophies/CROSS-PLATFORM.md b/contributingGuides/philosophies/CROSS-PLATFORM.md index a9b78d527085..d57992941b4c 100644 --- a/contributingGuides/philosophies/CROSS-PLATFORM.md +++ b/contributingGuides/philosophies/CROSS-PLATFORM.md @@ -17,10 +17,12 @@ A feature isn't done until it works on all platforms. Any platform-specific code ### - ReactNative bugs MUST be fixed upstream If the reason cross-platform code cannot be written is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths. +While upstream PRs are waiting to be merged, sometimes a patch can be used which is managed by the NPM package `patch-package`. Read more about it [here](https://github.com/Expensify/App?tab=readme-ov-file#adding-hybridapp-related-patches). + ### - Features that don't exist on all platforms MUST use NOOP shims If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing `if (platform=iOS) { }`, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic. -### - Platform code MUST be placed in dedicated files and folders +### - Platform specific code MUST be placed in dedicated files and folders Put all platform specific code in dedicated files and folders (see below) and reject any PR that attempts to put platform-specific code anywhere else. This maintains a strict separation between business logic and platform code. ## Platform-Specific File Extensions @@ -33,3 +35,11 @@ In most cases, the code written for this repo should be platform-independent. In **Note:** `index.js` should be the default and only platform-specific implementations should be done in their respective files. i.e: If you have mobile-specific implementation in `index.native.js`, then the desktop/web implementation can be contained in a shared `index.js`. `index.ios.js` and `index.android.js` are used when the app is running natively on respective platforms. These files are not used when users access the app through mobile browsers, but `index.website.js` is used instead. `index.native.js` are for both iOS and Android native apps. `index.native.js` should not be included in the same module as `index.ios.js` or `index.android.js`. + +### Supporting Mobile Web +The above platform-specific files only work because they are compiled when the app is built for the different platforms. This means that a different mechanism needs to be used for Mobile Web (since "mobile web" and "web" are the same at build time). + +It is also well known that different mobile browsers have different quirks and need to use different workarounds at times. + +#### - Mobile browser detection SHOULD never be used +If there is absolutely no other way to fix a bug, then the proper way of detecting the browser is using `@libs/Browser`. Use this as an absolute last resort and have the exception approved by several (ie. more than one) internal engineers. From 3364f3339dff7f3df4f0042d54c2db23546385ea Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 13 Aug 2025 10:04:20 -0600 Subject: [PATCH 3/4] Remove redudant text --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index e3a73ed08253..50f65dd03313 100644 --- a/README.md +++ b/README.md @@ -652,10 +652,6 @@ This application is built with the following principles. 1. **Cross Platform 99.9999%** This section has moved [here](contributingGuides/philosophies/CROSS-PLATFORM.md). - 1. A feature isn't done until it works on all platforms. Accordingly, don't even bother writing a platform-specific code block because you're just going to need to undo it. - 1. If the reason you can't write cross-platform code is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths. - 1. If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing if (platform=iOS) { }, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic. - 1. Put all platform specific code in dedicated files and folders, like /platform, and reject any PR that attempts to put platform-specific code anywhere else. This maintains a strict separation between business logic and platform code. ---- From 810d02c7f363169ce7aeda3be560d53900495056 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 20 Aug 2025 09:35:32 -0600 Subject: [PATCH 4/4] Update some paths and copy --- README.md | 2 +- contributingGuides/philosophies/CROSS-PLATFORM.md | 4 ++-- contributingGuides/philosophies/INDEX.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 50f65dd03313..3e7be6b854f6 100644 --- a/README.md +++ b/README.md @@ -651,7 +651,7 @@ This application is built with the following principles. 5. UI updates with data from the server 1. **Cross Platform 99.9999%** -This section has moved [here](contributingGuides/philosophies/CROSS-PLATFORM.md). +See details [here](contributingGuides/philosophies/CROSS-PLATFORM.md). ---- diff --git a/contributingGuides/philosophies/CROSS-PLATFORM.md b/contributingGuides/philosophies/CROSS-PLATFORM.md index d57992941b4c..02462024fc5f 100644 --- a/contributingGuides/philosophies/CROSS-PLATFORM.md +++ b/contributingGuides/philosophies/CROSS-PLATFORM.md @@ -14,10 +14,10 @@ The goal is: **Cross Platform 99.9999%** ### - Features MUST be implemented on all supported platforms A feature isn't done until it works on all platforms. Any platform-specific code blocks will be asked to be undone. -### - ReactNative bugs MUST be fixed upstream +### - React Native bugs MUST be fixed upstream If the reason cross-platform code cannot be written is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths. -While upstream PRs are waiting to be merged, sometimes a patch can be used which is managed by the NPM package `patch-package`. Read more about it [here](https://github.com/Expensify/App?tab=readme-ov-file#adding-hybridapp-related-patches). +While upstream PRs are waiting to be merged, a patch can be used which is managed by the NPM package `patch-package`. Read more about it [here](https://github.com/Expensify/App?tab=readme-ov-file#adding-hybridapp-related-patches). ### - Features that don't exist on all platforms MUST use NOOP shims If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing `if (platform=iOS) { }`, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic. diff --git a/contributingGuides/philosophies/INDEX.md b/contributingGuides/philosophies/INDEX.md index 59196f9c71f4..559df6c68f38 100644 --- a/contributingGuides/philosophies/INDEX.md +++ b/contributingGuides/philosophies/INDEX.md @@ -5,6 +5,6 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S "OPTIONAL" are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). ## Contents -* [Cross-Platform Philosophy](contributingGuides/philosophies/CROSS-PLATFORM.md) +* [Cross-Platform Philosophy](/contributingGuides/philosophies/CROSS-PLATFORM.md) * [Offline Philosophy](/contributingGuides/philosophies/OFFLINE.md) * [Routing Philosophy](/contributingGuides/philosophies/ROUTING.md)