Skip to content

Commit

Permalink
fix(DTXReactNativeSupport): JS and Content can load in any order
Browse files Browse the repository at this point in the history
Previously waitForReactNativeLoadWithCompletionHandler would
add an observer for "RCTJavaScriptDidLoadNotification",
then when notified would add a second observer for
"RCTContentDidAppearNotification", and after getting a notification
for both it would call the completion handler.

However, with the React Native new architecture the loading of
the JS and Content can happen in a different order (content first).
This would cause DetoxSync to get stuck when enabling synchronization.

Instead, we register all observers (JS, content, failure) one after
the other, and we use a race-free C11 atomic to count when we
receive a successful JS or Content notification.
After we receive both (or fail), we call the handler as before.
  • Loading branch information
tux3 committed Apr 30, 2024
1 parent f69efdb commit bb61ed5
Showing 1 changed file with 33 additions and 12 deletions.
45 changes: 33 additions & 12 deletions DetoxSync/DetoxSync/ReactNativeSupport/DTXReactNativeSupport.m
Original file line number Diff line number Diff line change
Expand Up @@ -212,23 +212,44 @@ + (void)waitForReactNativeLoadWithCompletionHandler:(void (^)(void))handler
{
NSParameterAssert(handler != nil);

__block __weak id observer;
__block __weak id observer2;

observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTJavaScriptDidLoadNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
[[NSNotificationCenter defaultCenter] removeObserver:observer2];
__block __weak id jsObserver;
__block __weak id contentObserver;
__block __weak id failObserver;

// JavascriptDidLoad and ContentDidAppear can happen in any order
// When we receive a notification (either of them), we set this to 1 (atomically)
// If it was already at 1, then we received both, and so we can call the handler
static _Atomic int successfulNotificationsReceived;
atomic_store(&successfulNotificationsReceived, 0);

jsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTJavaScriptDidLoadNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:jsObserver];

// If the flag was already at 1 then we just received the 2nd, so we call the handler
int expected = 0;
if (!atomic_compare_exchange_strong(&successfulNotificationsReceived, &expected, 1))
{
[[NSNotificationCenter defaultCenter] removeObserver:failObserver];
handler();
}
}];

observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTContentDidAppearNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
contentObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTContentDidAppearNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:contentObserver];

// If the flag was already at 1 then we just received the 2nd, so we call the handler
int expected = 0;
if (!atomic_compare_exchange_strong(&successfulNotificationsReceived, &expected, 1))
{
[[NSNotificationCenter defaultCenter] removeObserver:failObserver];
handler();
}];
}
}];

observer2 = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTJavaScriptDidFailToLoadNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
[[NSNotificationCenter defaultCenter] removeObserver:observer2];
failObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"RCTJavaScriptDidFailToLoadNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[NSNotificationCenter defaultCenter] removeObserver:jsObserver];
[[NSNotificationCenter defaultCenter] removeObserver:contentObserver];
[[NSNotificationCenter defaultCenter] removeObserver:failObserver];

handler();
}];
Expand Down

0 comments on commit bb61ed5

Please sign in to comment.