[Bridge] Consistently post "DidFailToLoad" notification when there's an error
Summary: Previously the bridge sometimes never fired RCTJavaScriptDidLoadNotification or RCTJavaScriptDidFailToLoadNotification if there was an error (for example, if the source code loaded but we couldn't inject the JSON config). This diff moves the error handling into a method called `stopLoadingWithError` that the bridge can call whenever there is an error. Also if the script failed to load, the BatchedBridge still called `executeSourceCode`. With this diff the `_loading` flag is set to NO when the script fails to load, and `executeSourceCode` returns immediately when `_loading` is false. This way the bridge does not try to execute JS when there is a loading error. Closes https://github.com/facebook/react-native/pull/2520 Github Author: James Ide <ide@jameside.com>
This commit is contained in:
parent
9b1f6c9e30
commit
3f4c7e40c6
|
@ -117,8 +117,15 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
||||||
dispatch_group_enter(initModulesAndLoadSource);
|
dispatch_group_enter(initModulesAndLoadSource);
|
||||||
|
__weak RCTBatchedBridge *weakSelf = self;
|
||||||
__block NSString *sourceCode;
|
__block NSString *sourceCode;
|
||||||
[self loadSource:^(__unused NSError *error, NSString *source) {
|
[self loadSource:^(NSError *error, NSString *source) {
|
||||||
|
if (error) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf stopLoadingWithError:error];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sourceCode = source;
|
sourceCode = source;
|
||||||
dispatch_group_leave(initModulesAndLoadSource);
|
dispatch_group_leave(initModulesAndLoadSource);
|
||||||
}];
|
}];
|
||||||
|
@ -131,7 +138,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
RCTProfileHookModules(self);
|
RCTProfileHookModules(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak RCTBatchedBridge *weakSelf = self;
|
|
||||||
__block NSString *config;
|
__block NSString *config;
|
||||||
dispatch_group_enter(initModulesAndLoadSource);
|
dispatch_group_enter(initModulesAndLoadSource);
|
||||||
dispatch_async(bridgeQueue, ^{
|
dispatch_async(bridgeQueue, ^{
|
||||||
|
@ -150,16 +156,24 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
// We're not waiting for this complete to leave the dispatch group, since
|
// We're not waiting for this complete to leave the dispatch group, since
|
||||||
// injectJSONConfiguration and executeSourceCode will schedule operations on the
|
// injectJSONConfiguration and executeSourceCode will schedule operations on the
|
||||||
// same queue anyway.
|
// same queue anyway.
|
||||||
[weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) {
|
[weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
|
||||||
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
|
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
|
||||||
|
if (error) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf stopLoadingWithError:error];
|
||||||
|
});
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
dispatch_group_leave(initModulesAndLoadSource);
|
dispatch_group_leave(initModulesAndLoadSource);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
|
dispatch_group_notify(initModulesAndLoadSource, dispatch_get_main_queue(), ^{
|
||||||
if (sourceCode) {
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
[weakSelf executeSourceCode:sourceCode];
|
if (sourceCode && strongSelf.loading) {
|
||||||
|
dispatch_async(bridgeQueue, ^{
|
||||||
|
[weakSelf executeSourceCode:sourceCode];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -172,23 +186,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
||||||
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
|
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
|
||||||
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
NSArray *stack = error.userInfo[@"stack"];
|
|
||||||
if (stack) {
|
|
||||||
[self.redBox showErrorMessage:error.localizedDescription
|
|
||||||
withStack:stack];
|
|
||||||
} else {
|
|
||||||
[self.redBox showErrorMessage:error.localizedDescription
|
|
||||||
withDetails:error.localizedFailureReason];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
|
||||||
object:_parentBridge
|
|
||||||
userInfo:userInfo];
|
|
||||||
}
|
|
||||||
|
|
||||||
_onSourceLoad(error, source);
|
_onSourceLoad(error, source);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,7 +280,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
object:self];
|
object:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setupExecutor
|
- (void)setupExecutor
|
||||||
{
|
{
|
||||||
[_javaScriptExecutor setUp];
|
[_javaScriptExecutor setUp];
|
||||||
|
@ -313,12 +309,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
[_javaScriptExecutor injectJSONText:configJSON
|
[_javaScriptExecutor injectJSONText:configJSON
|
||||||
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
|
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
|
||||||
callback:^(NSError *error) {
|
callback:onComplete];
|
||||||
if (error) {
|
|
||||||
[self.redBox showError:error];
|
|
||||||
}
|
|
||||||
onComplete(error);
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)executeSourceCode:(NSString *)sourceCode
|
- (void)executeSourceCode:(NSString *)sourceCode
|
||||||
|
@ -333,7 +324,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
|
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
|
||||||
if (loadError) {
|
if (loadError) {
|
||||||
[self.redBox showError:loadError];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self stopLoadingWithError:loadError];
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +345,28 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)stopLoadingWithError:(NSError *)error
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
if (!self.isValid || !self.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loading = NO;
|
||||||
|
|
||||||
|
NSArray *stack = error.userInfo[@"stack"];
|
||||||
|
if (stack) {
|
||||||
|
[self.redBox showErrorMessage:error.localizedDescription withStack:stack];
|
||||||
|
} else {
|
||||||
|
[self.redBox showError:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:userInfo];
|
||||||
|
}
|
||||||
|
|
||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL
|
||||||
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
||||||
|
|
Loading…
Reference in New Issue