As we have built our app in React Native, our understanding of how to build software has evolved. As our understanding grows, newer code uses newer techniques. Older code is often left un-updated. It can be difficult to orient oneself around what the current preferred practices are.
This document is a map. Not of Eigen at a specific time, but a map of how we got here and where we want to go next. This is a living document, expected to be updated regularly, of links to:
- Example code.
- Pull requests with interesting discussions.
- Conversations on Slack.
- Blog posts.
Links should point to specific commits, and not a branch (in case the branch or file is deleted, these links should always work). But it's possible that a file is outdated, that our understanding has moved on since it was linked to; in that case, please update this document.
The app is written in Objective-C and Swift, with React Native added in 2016. We only ship an iOS app, and do not yet use React Native for an Android app.
Objective-C and Swift (sometimes called "Native" code) are responsible for the following parts of the app:
- Sign up/in flow ("onboarding").
- Live Auctions Integration (LAI) view controller and networking.
- The Auction view controller.
- The SwitchBoard (see "SwitchBoard" section below) to navigate between view controllers.
- The top-level tab bar, and each tab's navigation controller.
- Deep-link and notification handling (via SwitchBoard).
- Analytics for Native UI.
- Initializing the React Native runtime.
Everything else is written in React Native.
New features should be built in React Native. The React Native runtime currently requires an existing user ID and access token to be loaded, and sign up/in is still handled in Native code.
- Why Artsy uses React Native
- All React Native posts on Artsy's Engineering Blog
- Some great React Native components:
- Partner is a simple top-level component.
- PartnerShows is a fragment container that uses FlatList to paginate through Relay data.
- Search is a functional component that loads data in response to user input.
We used to have many different renderX
functions throughout our components, but today we prefer to have a single render()
function in a component. See this PR for our rationale and a comparison of approaches.
We use TypeScript to maximize runtime code safety. In April 2020, we adopted TypeScript's strict
mode. This disables "implicit any" and require strict null checks. The change left a lot of comments like this throughout the codebase:
// @ts-ignore STRICTNESS_MIGRATION
Our goal is to reduce the number of STRICTNESS_MIGRATION
migrations checks to zero over time. We use CI tooling to require PRs never to increase the number. You can opt in to helping out by requiring all the files you change to fix all the migration comments by running the following command:
touch .i-am-helping-out-with-the-strictness-migration
Everything in src/
is React Native. Within this folder, things can be a bit of a mess. We are working on cleaning it up. Check back here for more later.
TODO: Figure out what we want our directory structure to be and define it here.
Data should be loaded from Metaphysics, Artsy's GraphQL server. Requests to Metaphysics should be made through Relay.
- Our use of styled-components was supplemented by styled-system in #1016.
- Example pull request migrating a component from styled-components to styled-system
Unit testing on Emission is a bit all over the place. Some top-level notes:
- We prefer
react-test-render
overenzyme
, and would ultimately like to removeenzyme
. - We prefer
relay-test-utils
over our existingMockRelayRenderer
. - We have native unit tests too. See
getting_started.md
- We don't like snapshot tests; they produce too much churn for too little value. It's okay to test that a component doesn't throw when rendered, but use
extractText
(or similar) to test the actual component tree.
Here are some great examples of what tests and test coverage should look like.
- Tests for Gene component
- Tests for Consignments submission flow
- Tests for Consignments photo-selection component interactions.
- Consignments Overview is a really complex component, so tests are broken into four test files:
- General component tests
- Analytics tests
- Local storage tests
- Image uploading tests
CollectionsRail
tests demonstraterelay-test-utils
.
Our React Native code ("Emission") is used by our Native code ("Eigen"). They used to be two repositories but were combined in February 2020. Traces of the separation remain. The structure we originally took is described in this blog post. Interop between JavaScript and Native can be tricky.
Most interactions are made through a "SwitchBoard" to open links. Other interactions are handled by the APIModules
, for example when Eigen needs to invoke some kind of callback.
- Switchboard routes defined in Eigen
- Emission switchboard to call out to Eigen
- Callbacks between JS and native code are set up here.
There is extensive inline documentation in our tracking code, including examples.