Jacek Pudysz | Copyright ©2024 | All rights reserved
Built with Astro 🚀
I understand how you feel. As a React Native developer, you’ve dedicated years to improve your skills and mastering every concept in the mobile world. Chances are, you started with a foundation in web-based ReactJS. You were intrigued by the possibilities of React Native, so you immersed yourself in its documentation, familiarized yourself with components like ScrollView and FlatList, and felt the excitement of creating your initial animation using the Animated API. You were amazed by functionalities such as Push Notifications and filled with joy as you took your first photo using the Camera module.
But sooner or later, there comes a time when you have to create your first native library. It might seem difficult and intimidating initially. From that point onward, you’ll need to grasp the basics of native Android, iOS, have a slight understanding of Objective-C, Kotlin, and how the React Native bridge functions. It can feel mystifying, leading you to hesitate when it comes to the idea of crafting your own native module. You might think, ”But what about Expo? Can’t I use it to quickly prototype my native module?” Indeed, Expo is a fantastic piece of software for that purpose. However, what about projects that can’t transition to Expo? How about truly understanding the concept of crafting native modules without the convenience of pre-built tools and simplified APIs?
In this post, I will focus on the iOS side of things. We’ll explore how things were in the past, how they are at present, and what we can expect going forward. We’ll kick things off with a quick overview of the history of React Native modules.
// do something sync
// do something async
If you have a background in web development, you’re likely familiar with the fact that serialization and deserialization processes take time, unfortunately causing your calls through the bridge to become slow and asynchronous. This means you would need to use constructs like Callbacks, Promises, Event emitters or the async/await syntax.
While this might be suitable for many scenarios, if your app heavily relies on communication via the bridge, performance problems can emerge. This raises a concern: With such asynchronous overhead, how can React Native truly live up to its “Native” name? How can we draw comparisons between React Native and native iOS or Android applications? Oftentimes, you might sense that the app running on your phone is built using React Native due to these factors.
With this fresh approach, we’re able to utilize C++ as the communication layer. Numerous functions that used to be asynchronous can now operate synchronously without blocking the thread. This opens up the possibility of constructing APIs in an incredibly versatile manner. The new architecture introduces only a marginal increase in code execution time, measured in mere milliseconds. There is no time-consuming serialization and deserialization of JSON. Thanks to this revamped bridge, we gain the capability to synchronously call any synchronous API from both iOS and Android. As a result, React Native, with its emphasis on code shareability, truly begins to shine!
Now, you might be wondering: How did they achieve this? How did Facebook devise a solution that managed to bypass such intensive serialization tasks? There’s a single magical term that holds the key: HostObject. While we’ll dive deep into HostObjects in the near feature, I’ll provide a fundamental overview of them in Part 4 of this series.
I know what you might be thinking - “I don’t want to learn C++! I consider myself more of a web developer than a native one.”. But don’t worry, learning C++ isn’t a necessity! While you can choose to delve into it, it’s not mandatory. Facebook has introduced a concept called Codegen, which takes care of generating C++ bindings for you. This way, you can concentrate on the actual implementation rather than the inner workings of the new bridge. We’ll explore Codegen in depth and create our first module in part 2 of this series.
However, let’s face it, life rarely unfolds that straightforwardly. You can bypass C++ directly, thanks to generated bindings, but there’s one more aspect to consider. If you’re serious about your library and want to support every app within the React Native ecosystem, both old and new architectures need attention. What does this mean?
In essence, you have the flexibility to enable or disable the new architecture on a per-project basis. Granted, there are apps that have adopted the new architecture and managed to replace libraries without Fabric support. Yet, the current state of the React Native ecosystem reveals that the majority of apps still adhere to the Paper architecture.
The new architecture made its debut with React Native 0.68, and Facebook is putting its best efforts into popularizing this novel approach. To this end, there’s even a dedicated working group that assists library creators in transitioning their modules to Fabric. Additionally, the official React Native documentation has mentioned that it will soon deprecate the Old Bridge.
“Native Module and Native Components are our stable technologies used by the legacy architecture. They will be deprecated in the future when the New Architecture will be stable.” ~Facebook
However, it’s important to recognize that moving from the old to the new architecture will take some time before it becomes universal. This transitional phase is similar to Apple’s decision to replace Intel chips with the more advanced M1 chips. That shift took approximately 2 years to complete. It’s my hope that both this series and your engagement with it will contribute a small but significant part to the larger effort of establishing the new architecture. With this momentum, it’s possible that around 1 year from now, we could potentially bid farewell to the Paper architecture altogether.
Even though React Native 0.68 introduced the new architecture, we initially needed to augment our native code with C++ functions for registering and interacting with Fabric. In the subsequent React Native releases, both Facebook and the community played a significant role in streamlining this process by completely removing these bindings from the visible code. You can even compare versions 0.68 to 0.72 using the React Native Upgrade Helper.
Facebook invested considerable time to ensure a seamless transition between the Paper and Fabric architectures. In the most recent React Native release, we’re given the ability to reuse legacy (Paper) components in the apps with new architecture enabled. This is a game changer as previously every native component had to be migrated to Fabric. Check the details about this approach here.
Let’s circle back to the concept of crafting your new native module. Is it challenging? I believe so. It means learning a lot of new things. You might make mistakes and occasionally feel alone when troubleshooting issues. But don’t worry, there are people who are working hard to help. Take a look at individuals like mrousavy, Oscar Franco or companies like Software Mansion, Callstack and even the Expo team. Their efforts provide us with a gateway to the realm of advanced bridging, enabling us to craft our custom native modules.
In the upcoming posts, we’ll delve deep into the New Architecture. I’ll guide you through understanding how Codegen works, explaining what TurboModules are, exploring JSI, clarifying how Fabric View operates, and demystifying the mysterious HostObject. We will also construct a module using a Sweet API (Expo Native Modules), giving you a sneak peek into how building your native modules can be simplified.
Please accept my invitation to this fascinating journey. See you in the next post 👋
Since this is my first post in the world of programming, it’s only fitting to refer to it as the “Hello World” post. I’ve chosen to dive right into the React Native Bridging, so let’s greet the world in the languages we’ll be discussing throughout this blog:
console.log("Hello World from Typescript")
NSLog(@"Hello World from Objective-C!");
std::cout << "Hello World from C++!" << std::endl;
println("Hello World from Kotlin!")
print("Hello World from Swift!")
Subscribe to the React Native Crossroads newsletter
Sponsor me on Github
Buy me a coffee
Share it on Twitter
Jacek Pudysz | Copyright ©2024 | All rights reserved
Built with Astro 🚀