The Story of Rewriting React-Native-Opentok and the Challenges Encountered

Authors
Piotr Drapich
Software Engineer
@
Callstack

At Callstack we have open source meetings each month. At one of the past meetings we came up the with idea to re-maintain our old project called react-native-opentok, because it was a bit dusty and forgotten. I thought: “Hmm… it should be easy to make it great again.” but after discussing with the team, we decided to rewrite the whole library due to changes in react-native. When working on it, we faced many challenges on both platforms. Finally, we solved all of them :)

This article contains useful steps that you can perform in order to debug and fix real-life issues on a native side.

What is OpenTok?

The OpenTok platform, developed by TokBox, makes it easy to embed high-quality interactive video, voice, messaging, and screen sharing into web and mobile apps.

It is well documented and TokBox provides client and server SDKs for following platforms:

Thanks to that we were able to use SDK and wrap it into our react-native module. From a technical side, it provides us with a system to manage sessions, subscribers and publishers. We wanted to bring Android and iOS support. Here are the most annoying issues that we have encountered on each platform respectively while writing clients:

Android: Updating native view doesn’t work

We had a native view which we wanted to update after we connected to a session and received a stream. We were able to connect with a session, add a subscriber to the session, get a stream, but when we tried to add a new view to the existing view after getting the stream, nothing happened. It worked perfectly when we changed the orientation of a device.

updating native view doesn't work on Android

This fact helped us a lot in resolving this issue. I created a button which was changing the size of our view and after a click, I can see the stream on the device. So the solution was simple, I had to request an update of view from the native side. I called the function requestLayout right after I added a view with my stream but with no effect. It forced me to do a little research. I found that the overriding requestLayout method and forcing it to update my view is a perfect solution.

private final Runnable mLayoutRunnable = new Runnable() {
@Override
public void run() {
measure(
MeasureSpec.makeMeasureSpec(getWidth(),MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)
);
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
@Override public void requestLayout() {
super.requestLayout();
post(mLayoutRunnable);
}
view raw requestLayout hosted with ❤ by GitHub

I added this code to my View and now everything works as I expected. View is updated when I call requestLayout on the instance.

iOS: EXC_BAD_ACCESS

One of our goals was supporting multiple sessions. We created a Session Manager using a singleton pattern and had this issue because of that singleton. Our Subscriber view implements SessionDelegate class which means we can set this view as a delegate in session and listen on events i.e. when session receives a stream, session instance invokes method <rte-code>onStreamReceived<rte-code> on pointer to delegate. And everything works perfectly but… I get an error <rte-code>EXC_BAD_ACCESS<rte-code> when refreshing my application using <rte-code>cmd + R<rte-code>. This error means that you are sending a message to an object that has already been released.

When that block of memory is no longer mapped for your application or, put differently, that block of memory isn’t used for what you think it’s used, it’s no longer possible to access that chunk of memory. When this happens, the kernel sends an exception (EXC), indicating that your application cannot access that block of memory (BAD ACCESS).

The worst thing is that the only information which I get with this error is a hex representation of the pointer i.e. <rte-code>0x1933201<rte-code>. Don’t worry, NSZombie comes with help. Thanks to NSZombie Xcode can tell you what object you were trying to access, making the search for the problem that much easier. Enabling zombies in Xcode is very easy.

  1. Click the active scheme in the top left and choose Edit Scheme.
  2. Select Run on the left.
  3. Open the Diagnostics tab at the top.
  4. Tick the checkbox labeled Enable Zombie Objects.

Now Xcode’s Console will give you more complex message i.e.

[RNOpenTokSubscriberView respondsToSelector:] message sent to deallocated instance 0x1933201
view raw zombie_objects hosted with ❤ by GitHub

Thanks to this information I found my error and realized that when I refresh the application using <rte-code>cmd + R<rte-code> , my singleton instance of session manager is still the same as before reload and still has session objects with a pointer to old subscriberView. One of those session wanted to call a method on a deallocated subscriberView instance. I had to clean all the pointers in subscriberView’s <rte-code>dealloc<rte-code>. So remember when you reload the JS part of the application, a new instance of your native module will be created but if you are using singleton it will be the same instance as before reload.

Summary

As I wrote at the beginning we solved all of our issues but some of them took a lot of time. I think the hardest thing in implementing native modules is that the platforms are totally different and almost always you will meet a problem which exists only on one platform. Sometimes you are not able to standardize your API on both platforms in a native part which forces you to do it in theJS part like i.e. with EventEmitter but I’ll write the next article about this. At this moment I’m happy that I joined the react-native-opentok team and I’m glad to write that the new react-native-opentok library is coming very soon!

Insights

Learn more about Open Source

cover

Aug 7Article

Visualizing Open Source Licenses to Analyze Compliance and Reduce Legal Risk

Discover how license-kit visualize maps and analyzes your dependency graph, helping you spot license issues, duplicates, and restrictive packages with interactive views, stats, and AI-powered summaries.

cover

Mar 26Podcast

How Definitely Typed Changed TypeScript Forever

Discover the story behind Definitely Typed with its creator, Boris Yankov, and learn how it became essential for type-safe JavaScript in the TypeScript ecosystem.

cover

Oct 14Article

Introducing the OMH Cloud Storage Module

Discover the OMH Cloud Storage Module. This open-source solution simplifies cloud storage integration for React Native apps.

cover

Jul 12Article

Introduction to the React Native OMH Maps Library

Discover how the React Native OMH Maps library simplifies map integration with cross-platform compatibility, ease of use, and customization options.

cover

Apr 22Podcast

Behind the Scenes of React Native 0.74 Release

Join the release crew members to find out what new features and improvements React Native 0.74 brings to the cross-platform development world.

cover

Apr 27Podcast

Callstack OSS Update

April 2023 Callstack OSS Update: RFC for Expo Config Plugins, Re.Pack & Paper news.

cover

Dec 6Article

React Native Paper 5.0 - What’s New?

React Native Paper 5.0 is here! Dive into the latest updates and improvements.

cover

Feb 8Article

Stripe React Native: the Most Common Questions - Tipsi Migration Guide, Part 5

Check out the list of the most common questions about Stripe react Native Library prepared by Jakub Kłobus, software engineer at Callstack.

cover

Feb 6Article

Stripe React Native Migration Function by Function - Tipsi Migration Guide, Part 3

In the third part of the Tipsi Migration Guide we describe the Stripe React Native migration function by function.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.