In a few words: It’s been amazing. We think they are great developers and we learned a lot from them. They communicate efficiently with the stakeholders and provide helpful feedback and suggestions. Even though they are external contributors, we consider them as colleagues and part of the team.
<cyan>React Native<cyan> integration
The kiwi.com app helps organize your air travel in a simple and safe way. It offers the best prices for your airline tickets and guarantees to cover missed connections. The client had a mobile app for both platforms - iOS and Android - with totally separated codebases. Within these native applications, some screens were presented as a WebView. The goal of our Kiwi project was to switch those views into React Native using a single codebase. In other words, it was all about integrating React Native into two fully native production apps.
Issues <cyan>to solve<cyan>
We identified several problems; the major ones were as follows:
The user opened the app and when it was time to click a button to navigate to a React Native screen, the user had to wait for the JS to load. If the user navigated back and clicked again, they had to wait one more time.
The app had problems integrating the native side of things with react-navigation. On Android the major problem was the back button interaction. On iOS, the swipe back gesture.
The main problem here was keeping the React Native version in sync as we needed to update it in the 3 different repositories at the same time.
The client was also interested in having CodePush.
The client wanted to be able to reuse some existing Native code in React Native without having to re-implement it.
<cyan>Techniques<cyan> to adopt
We decided to go for a brownfield approach and apply the following techniques for the following issues:
The solution for both platforms is written in a different way but the idea is the same: Using the singleton pattern.
iOS
We initialize the RCTBridge when the app starts (not when the user navigates to a React Native screen) and we create a singleton to share it whenever it is needed.
Android
We override the ReactNativeHost class and we configure anything related to our ReactInstanceManager there. In the Application level, that is, a class extending android.app.Application, we store there our reactNativeHost instance to be reused and shared. This way, it does not rely on a Context that can be destroyed (like if we were initiating the ReactInstanceManager in some Activity). We also make use of createReactContextInBackground().
iOS
On iOS, we needed to disable the native swipe back gesture if we were in a nested react-navigation screen (so it could be handled in the JavaScript side). On the other hand, if we were in the first React Native screen (not nested), the gesture needed to be enabled. Therefore, we created a native module so we could enable/disable native gesture from React Native. For such, we used the interactivePopGestureRecognizer.
Moreover, in order to go back from a React Native screen to a native one, we expose a method in our native module to close the current view controller (which is basically calling popViewControllerAnimated.
Android
The Android Activity that hosts the React Native View needs to implement DefaultHardwareBackBtnHandler from the react package. This is explained in detail in Integration with Existing Apps documentation.
However, we should also be able to navigate back from a React Native screen to a native one using any kind of button (like the back arrow on the Toolbar). For that, we wrote a Native Module for React Native to be able to go back to native whenever is necessary. This is as simple as calling the .finish() method of an Activity.
The following would be triggered by GitlabCI in every merge to master so it was fully automatic.
iOS
Here, we had a script that would package everything into a .framework. These were the requirements from the iOS Native team, so they didn’t have to use CocoaPods and build React. We uploaded that .framework into Github Releases and the native team just had to copy and paste it into their project.
Moreover, in order to go back from a React Native screen to a native one, we expose a method in our native module to close the current view controller (which is basically calling popViewControllerAnimated.
Android
On Android there were more steps:
For every native dependency, build the library and deploy it into a private maven repository For every new version of React Native, build it and deploy it to a private maven repository We wrote generic classes and the code responsible for the bridge, navigation, etc. and we made a library out of it, that was published to maven too. Here we were also building the JS code and attach it as an asset The native app was just consuming the library without having a direct dependency to react-native.
We also implemented CodePush so GitlabCI was able to release a new version whenever conditions matched.
<cyan>Faster for the users<cyan> and more developer friendly
In short: Delivering a brownfield solution for both platforms which keeps a natural native feeling without having to rely on a WebView. Plus it can work offline!
Need help with React or React Native? Let us know!
I hereby agree for sending me by Callstack.io Sp. z o.o. with seat in Wrocław, by means of electronic communication to the e-mail address indicated by me, commercial information, within the meaning of the Act of 18 July 2002 on the provision of electronic services. For details see our Privacy Policy.