Story of rewriting react-native-opentok and challenges that we have encountered down the road…

Piotr Drapich
Callstack Engineers
5 min readOct 10, 2017

At Callstack we have open source meetings each month. At one of the past meetings we came up 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 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 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 connect to a session and receive 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 an orientation of a device.

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 function requestLayout right after I added a view with my stream but with no effect. It forced me to little research. I found that the overriding requestLayout method and force 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);
}

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 Session Manager using singleton pattern and had this issue because of that singleton. Our Subscriber view implements SessionDelegate class what means we can set this view as a delegate in session and listen on events i.e. when session receive a stream, session instance invokes method onStreamReceived on pointer to delegate. And everything works perfect but… I get an error EXC_BAD_ACCESS when refresh my application using cmd + R. 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 pointer i.e. 0x1933201. 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.
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

Thanks to this information I found my error and realized that when I refresh application using cmd + R , 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 method on deallocated subscriberView instance. I had to clean all pointers in subscriberView’s dealloc. So remember when you reload JS part of application new instance of your native module will be created but if you are using singleton it will be the same instance as before reload.

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 JS part like i.e. with EventEmitter but I’ll write next article about this. At this moment I’m happy of that I joined to the react-native-opentok team and I’m glad to write that the new react-native-opentok library is coming very soon! Thanks for your time, I hope this article was helpful.

--

--