Slide 1

Slide 1 text

Martin Konicek Facebook, React Native mkonicek martinkonicek Reactive conf
 Bratislava, Slovakia
 November 4, 2015 Under the hood of
 React Native

Slide 2

Slide 2 text

Overview Intro
 Building the Ads Manager & Sharing Code Architecture & Extensibility Open Source Process

Slide 3

Slide 3 text

React Native React Native is a library for building native apps with React. We are at a React conference and there were other talks about React Native, therefore I’ll assume you’re already familiar with React and basics of React Native.

Slide 4

Slide 4 text

Let’s have a quick look at the main benefits of React Native compared to traditional mobile development. 
 If you’re developing for the web, one thing you’re used to is that you can reload to quickly see your changes. You probably know that React Native gives you that same experience. Compare that to compiling and copying the binary to the device every time you make a change.

Slide 5

Slide 5 text

The way this works is there is a local development server (a custom packager, you can imagine it as something similar to Webpack) that runs on your machine and serves a bundle containing all the JS needed to run your app. On a small change it can return a new bundle really fast. GET /index.bundle

Slide 6

Slide 6 text

Standardized APIs
 Another big thing for me personally is that I don’t have to learn a completely different set APIs for Android and iOS.
 
 For example, I don’t need to learn twice how to write to disk, make network requests, or show a scrollable list of items.

Slide 7

Slide 7 text

Standardized APIs
 ... —————————— styles = { row: { flexDirection: 'row', alignItems: 'center', padding: 5, backgroundColor: 'white', }, }; That’s also true for layout, which is done exactly the same way on Android and iOS using Flexbox that you already know from the web.

Slide 8

Slide 8 text

Layout-only View Removal Before After What’s interesting is React Native uses one Android-specific optimization related to layout. These two UIs look the same but the view hierarchy on the right is simpler.

Slide 9

Slide 9 text

Layout-only View Removal Before After Goal The way it works is we remove all the native views that only define layout but are not visible in any way (e.g. don’t have color). This means Android has to do less work traversing the view hierarchy which means a more performant UI.

Slide 10

Slide 10 text

Building the Ads Manager Now that we’ve done a very quick intro to React Native let’s look at how we built the Ads Manager. This was the first app built fully in React Native both for iOS and Android.

Slide 11

Slide 11 text

Here are some screenshots from the Ads Manager on iOS.

Slide 12

Slide 12 text

And here’s what the Android version looks like.
 Both apps have native look and feel, I encourage you to try them out. You’ll need to have ads on Facebook to have the full experience but you can play with it even without having ads.

Slide 13

Slide 13 text

3 month iOS release→Android release Same dev team 85%+ code shared What was the experience for the Ads Manager team like?
 
 It only took them 3 months to build the Android app once the iOS version was finished. It was one team of people building for both platforms. Most people on the team had mainly JS experience, some iOS and some Android experience.
 
 It turned out they could even share most of the code between platforms. Note that code sharing wasn’t an explicit goal - React Native is learn once write anywhere, not write once run anywhere.

Slide 14

Slide 14 text

And here is how the Ads Manager team felt about that.

Slide 15

Slide 15 text

How do you share code? OK, even though sharing code wasn’t an explicit goal it sounds really cool. How do you do that?

Slide 16

Slide 16 text

AdDetailView.js The easy case is when a part of the UI looks and works the same way on iOS and Android.
 Like in this screen, where we’re sharing all the UI and data fetching logic.

Slide 17

Slide 17 text

AdDetailView.js But within a shared JS file you can still check what platform you’re running on. On this slide notice another interesting thing - AdsManagerText - a component that defines a consistent look for text across the whole app, making it easy to change it in one place.

Slide 18

Slide 18 text

To take another example, look at the main Ads Manager screen on iOS and Android. There’s a ScrollView that has the same contents on both platforms but you can see that it has different pull-to-refresh behavior, specific to the platform.

Slide 19

Slide 19 text

RefreshableScrollView.ios.js On iOS, the implementation might look like this, where we put an Activity indicator above the ScrollView. (This is just a simplified example.)
 Note the .ios.js file extension.

Slide 20

Slide 20 text

RefreshableScrollView.android.js On Android we use the standard Android view called SwipeRefreshLayout.

Slide 21

Slide 21 text

CampaignsView.js Finally, since the RefreshableScrollView has the same API on both platforms we can simply use it on both platforms.

Slide 22

Slide 22 text

How does it work?

Slide 23

Slide 23 text

GET /index.bundle?platform=ios GET /index.bundle?platform=android You saw at beginning of the talk there’s the packager which serves JS to your application.

Slide 24

Slide 24 text

.js, .ios.js .js, .android.js The packager chooses the JS files based on the platform when creating the bundle. Simple.

Slide 25

Slide 25 text

Architecture
 (Android) iOS: @tadeuzagallo Now we’ll go a little bit deeper and look how React Native runs your code.
 It’s important to note I’ll focus on Android here but the iOS architecture is very similar. You can read about the iOS architecture in a blogpost by my colleague Tadeu.

Slide 26

Slide 26 text

Native JS VM Java/C++ Bridge (C++/Java) At the core of React Native is a bridge that lets native code calls JS and vice versa. 
 
 There’s a JS VM (we use JavaScriptCore) running your code. On Android, JavaScriptCore needs to ship with your application which means a Hello World Android app is about 3.5MB. On iOS JavaScriptCore is part of the system.

Slide 27

Slide 27 text

Native JS VM Java/C++ Bridge (C++/Java) A common question I get about the architecture of React Native is whether the application code runs in a WebView. There is no WebView, it’s JS running in a VM and controlling native UI.

Slide 28

Slide 28 text

Java JS Bridge Now let’s zoom in. This is the same as on the previous slide, native on the left, JS on the right.

Slide 29

Slide 29 text

Java JS Bridge AppRegistry.runApplication(   ‘MyApp’,   new  WritableMap()); require(‘AppRegistry’).runApplication(‘MyApp’,  {}); [1, 1, [‘MyApp’, {}]] It’s important to realize that we always start in native. In this case native decided to start your app. It sends a function id and arguments to JS using our custom JSON-based protocol over the C++ bridge.


Slide 30

Slide 30 text

Java JS Bridge require(‘AppRegistry’).runApplication(‘MyApp’,  {}); UIManager.createView(2,  ’View’,  {…});
 UIManager.createView(3,  ’Text’,  {…});
 … UIManagerModule#createView(2,  ‘View’,  …)   View  newView  =  new  View();   UIManagerModule#createView(3,  ‘Text’,  …)   TextView  newView  =  new  TextView();   … [[2, 3, [2, ‘View’, {…}]], [2, 3, [3, ‘Text’, {…}]]] JS calls that function which in turn leads to a bunch of calls from JS to native. For example, create a TextView, send a network request. These are all batched together and sent back to native asynchronously.


Slide 31

Slide 31 text

Java JS Bridge require(‘AppRegistry’).runApplication(‘MyApp’,  {}); UIManager.createView(2,  ’View’,  {…});
 UIManager.createView(3,  ’Text’,  {…});
 … [[2, 3, [2, ‘View’, {…}]], [2, 3, [3, ‘Text’, {…}]]] The calls are batched together so we don’t pay the overhead for each individual call. However, in some cases, such as when JS is doing lots of work, it might be better to split the batch into several batches. Imagine JS wants to create a few views, then read something from disk and do lots of computation, then update more views. It can be better to flush the queue of calls (see MessageQueue.js) into native early - this way the person using your app can see something on the screen meanwhile JS is still doing work.

Slide 32

Slide 32 text

UI Event Queue JS Event Queue Native Modules Event Queue In the previous slide we said the calls between native and JS are asynchronous. To explain that let’s look at the threading model.
 
 There’s the main thread with a queue of events. This is the Android main thread. Then there are two additional threads - one that runs operations on native modules and one that runs your JS (on iOS this works a little bit differently, refer to Tadeu’s blogpost). Each thread is processing a separate queue of events.

Slide 33

Slide 33 text

UI Event Queue JS Event Queue Native Modules Event Queue Touch Event Now imagine the OS told us (on the native main thread) there’s a touch event.
 
 The touch event here serves just as an example. If you are interested in how touch handling works refer to the awesome talk by @alex_frantic in the Videos section on the React Native website.

Slide 34

Slide 34 text

Handle Event
 -> bridge -> Runs JS Touch Event Touch Event UI Event Queue Native Modules Event Queue JS Event Queue Based on the touch event we add an event to the JS queue. The JS thread synchronously calls your application code via the bridge. This runs your JS which calls e.g. setState and render.


Slide 35

Slide 35 text

Handle Event
 -> bridge -> Runs JS Handle Event
 -> bridge -> Runs JS Touch Event Touch Event UI Event Queue Native Modules Event Queue JS Event Queue Dispatch View Updates Update UI Dispatch View Updates Update UI As you saw in the bridge overview, your code returns operations to be done by native modules. These can be things like adding, removing or updating native views, sending network requests etc.
 
 Then on the native modules thread we calculate new layout and finally update the views that need to change, on the main thread. Why not do layout in JS? We need to measure text - only native can do that.
 
 The whole cycle from the touch event to updating the UI should ideally happen within 16ms: this is how JS- driven animations work too but instead of a touch event there’s a timer event that fires on every frame.

Slide 36

Slide 36 text

Here’s an example of an animation created using the Animated API. Every frame a timer fires in native, JS computes new positions and tells native to update the views.

Slide 37

Slide 37 text

In some cases when JS is busy doing other work this can cause dropped frames. A workaround for this is to use the InteractionManager to delay work until animations have finished.
 
 In the future we might look into offloading animations created using the Animated API to run entirely in native.

Slide 38

Slide 38 text

Modularity Another important thing to know about the architecture is that it is very modular. There are two important abstractions that define what’s exported from native to JS: Modules and View Managers.

Slide 39

Slide 39 text

Modules 
 React.NativeModules.Dialog.show(‘♥  Bratislava’) DialogModule#show(“♥  Bratislava”) => A module has some state and methods that JS can call.
 In this example, we’re calling a Dialog module in JS which translates to a call on the native dialog module in Java.

Slide 40

Slide 40 text

View Managers   =>    new  TextView(getContext())   =>    new  Switch(getContext()) View managers define how JS views map to native views. When you specify you want to render a Switch in JS there’s a View Manager that knows how to do that and creates an Android Switch.

Slide 41

Slide 41 text

View Managers   =>    new  TextView(getContext())   =>    new  Switch(getContext()) The coolest thing about this is that you can easily define your own view managers and modules. This is my other favorite part about React Native - you can simply drop down to native code when needed. … define your own

Slide 42

Slide 42 text

Here is the code of MainReactPackage. It simply defines modules and view managers that are available to any app. You can see things like storage, network, Image or ScrollView.

Slide 43

Slide 43 text

MainActivity.java Let’s say you’ve implemented support for native maps. To start using the new native feature in your app, create a simple package with you view manager. Then simply add that package to your app’s MainActivity.

Slide 44

Slide 44 text

https://react.parts And what’s even cooler is you can then publish your package to npm and register it on react.parts to make it easily discoverable. Then everyone can find and use your code in their apps.
 
 The process of creating and linking native modules could be made smoother, we plan to work on that.

Slide 45

Slide 45 text

Open Source This brings me to the next topic which is our Open Source process.

Slide 46

Slide 46 text

If you look at our github repo an interesting thing to notice is we’ll soon have 400 unique contributors to the project.

Slide 47

Slide 47 text

Releases The first important thing to understand about the Open Source process is how releases work.

Slide 48

Slide 48 text

2 weeks of development release branch: e.g 0.14 2 weeks of stability master publish to npm e.g. 0.14.0-rc Every two weeks we cut a release branch from master and publish to npm so you can try out the new release candidate.

Slide 49

Slide 49 text

2 weeks of development publish to npm, e.g. 0.14.0 release branch: e.g 0.14 2 weeks of stability 2 weeks of development master publish to npm e.g. 0.14.0-rc We cherry-pick bug-fixes and sometimes small features from master during the following two weeks, then publish to npm and the whole process repeats.

Slide 50

Slide 50 text

For every version you can find release notes on github.
 Thank you James Ide (@JI) for taking the time to write these!

Slide 51

Slide 51 text

Exporting commits to github The next thing that’s useful to understand is how we sync code between the internal fb repo and github.

Slide 52

Slide 52 text

If you go to github and look at the commits, you’ll see commit messages like this one. What are the differential revision and sync id?

Slide 53

Slide 53 text

To explain that we first have to explain how we store the code internally. The source of truth for the React Native codebase is an internal Mercurial repo called fbsource. Here’s a simplified illustration of its layout. The react- native-github folder is a copy of what you see on github.
 
 We also have some Facebook-specific extensions like error reporting to the Facebook backend and of course there are apps like the Ads Manager, for example.

Slide 54

Slide 54 text

The way we commit code internally is we use a tool called Phabricator. Each code change is submitted to Phabricator and is called a “diff”. It is very similar to a pull request on github. A diff has to be reviewed by at least one person.
 
 Phabricator is integrated with our CI system - what you see at the bottom are test results. A lot of these tests are commit-blocking.

Slide 55

Slide 55 text

Once a diff has been committed (AKA it “has landed”) we create a patch only for those files that are open source (in the react-native- github folder), apply this to the github repo and push. This is done by a job that runs every minute.

Slide 56

Slide 56 text

And again, here’s what the diff looks like once it has been landed and exported to github.

Slide 57

Slide 57 text

Pull Requests OK, we’ve talked about getting code from the facebook codebase to github. What about the other way?

Slide 58

Slide 58 text

Here’s what happens when you send a pull request.

Slide 59

Slide 59 text

We have a bot that looks at files touched by the pull request and mentions people who are likely to be the best reviewers for the PR.

Slide 60

Slide 60 text

Once the code review is done and we’re happy with the PR, we comment ‘shipit’ which imports the code into Phabricator.

Slide 61

Slide 61 text

Here is a diff created from that pull request. 
 The most important thing in this slide are the Ads Manager tests. A pull request can affect closed- source apps and we need to make sure everything works when we merge the code into the fb repo.

Slide 62

Slide 62 text

Once the code is merged internally it gets exported to github like any other commit, keeping the original author of the PR which is important.

Slide 63

Slide 63 text

And as you can see people were happy this one got merged

Slide 64

Slide 64 text

SUMMARY To sum it up: A pull request gets turned into a diff, is tested and merged into the fb codebase and then exported to github like any other internal commit.

Slide 65

Slide 65 text

How to help

Slide 66

Slide 66 text

If you want to send a PR and looking for a place to get started, look for issues labeled ‘Good first task’.

Slide 67

Slide 67 text

You’ll probably notice the number of issues is too high. One reason is a lot of those issues are really questions. It would be awesome to have a way to put a banner on github saying those questions are best asked on Stack Overflow.

Slide 68

Slide 68 text

Please use Stack Overflow to ask & answer questions. It’s a perfect product for this.

Slide 69

Slide 69 text

Another thing about github issues is that they have no voting system. We’d really like to know what the most important things are for you guys. We started using a website called Product Pains.
 
 You help us by voting, submitting new issues there and above all helping fix the most important ones.

Slide 70

Slide 70 text

You can see that the top one is to get Android to feature parity with iOS. This means open sourcing more of the views and modules we’ve been using internally. 
 
 To do that we need to clean up the APIs and add good examples. I’m working on this.

Slide 71

Slide 71 text

Another thing you can help us with is documentation.
 
 Here’s an old version of documentation for one of the most common things people try to do - using an Android device for development. After a few conversations on github and Twitter I realized:

Slide 72

Slide 72 text

.. these were the points where it was easy to get stuck. It’s important to note this particular part of the docs has been improved only thanks to the feedback on Twitter. We’ve written the Android docs but we are Android engineers so it’s often not obvious what’s difficult. You guys are in the best position to tell us what needs to be clarified.
 
 Tweet at @martinkonicek or even better send pull requests that improve documentation every time you get even a little bit stuck. Happy to review them.

Slide 73

Slide 73 text

Thanks! @martinkonicek Thanks to all of you, the community around React Native is just amazing.
 
 This is the project with the most potential I’ve ever worked on and I’m very excited to see what we’ll build together.