15d8188f5SBen Roth// Copyright 2015-present 650 Industries. All rights reserved.
25d8188f5SBen Roth
35d8188f5SBen Roth#import "EXAppViewController.h"
45d8188f5SBen Roth#import "EXErrorRecoveryManager.h"
55d8188f5SBen Roth#import "EXKernel.h"
65d8188f5SBen Roth#import "EXKernelAppRecord.h"
75d8188f5SBen Roth#import "EXReactAppManager.h"
85d8188f5SBen Roth#import "EXReactAppExceptionHandler.h"
95d8188f5SBen Roth#import "EXUtil.h"
105d8188f5SBen Roth
115d8188f5SBen Roth#import <React/RCTBridge.h>
125d8188f5SBen Roth#import <React/RCTRedBox.h>
135d8188f5SBen Roth
145d8188f5SBen RothRCTFatalHandler handleFatalReactError = ^(NSError *error) {
155d8188f5SBen Roth  [EXUtil performSynchronouslyOnMainThread:^{
165d8188f5SBen Roth    EXKernelAppRecord *record = [[EXKernel sharedInstance].serviceRegistry.errorRecoveryManager appRecordForError:error];
175d8188f5SBen Roth    if (!record) {
18e7ac6e4fSBen Roth      // show the error on Home or on the main standalone app if we can't figure out who this error belongs to
19e7ac6e4fSBen Roth      if ([EXKernel sharedInstance].appRegistry.homeAppRecord) {
205d8188f5SBen Roth        record = [EXKernel sharedInstance].appRegistry.homeAppRecord;
21e7ac6e4fSBen Roth      } else if ([EXKernel sharedInstance].appRegistry.standaloneAppRecord) {
22e7ac6e4fSBen Roth        record = [EXKernel sharedInstance].appRegistry.standaloneAppRecord;
23e7ac6e4fSBen Roth      }
245d8188f5SBen Roth    }
255d8188f5SBen Roth    if (record) {
265d8188f5SBen Roth      [record.viewController maybeShowError:error];
275d8188f5SBen Roth    }
285d8188f5SBen Roth  }];
295d8188f5SBen Roth};
305d8188f5SBen Roth
315d8188f5SBen RothNS_ASSUME_NONNULL_BEGIN
325d8188f5SBen Roth
335d8188f5SBen Roth@interface EXReactAppExceptionHandler ()
345d8188f5SBen Roth
355d8188f5SBen Roth@property (nonatomic, weak) EXKernelAppRecord *appRecord;
365d8188f5SBen Roth
375d8188f5SBen Roth@end
385d8188f5SBen Roth
395d8188f5SBen Roth@implementation EXReactAppExceptionHandler
405d8188f5SBen Roth
415d8188f5SBen Roth- (instancetype)initWithAppRecord:(EXKernelAppRecord *)appRecord
425d8188f5SBen Roth{
435d8188f5SBen Roth  if (self = [super init]) {
445d8188f5SBen Roth    _appRecord = appRecord;
455d8188f5SBen Roth  }
465d8188f5SBen Roth  return self;
475d8188f5SBen Roth}
485d8188f5SBen Roth
495d8188f5SBen RothRCT_NOT_IMPLEMENTED(- (instancetype)init)
505d8188f5SBen Roth
5171a1d7edSStanisław Chmiela- (void)handleSoftJSExceptionWithMessage:(nullable NSString *)message
5271a1d7edSStanisław Chmiela                                   stack:(nullable NSArray<NSDictionary<NSString *, id> *> *)stack
535d8188f5SBen Roth                             exceptionId:(NSNumber *)exceptionId
54*529650a2SKudo Chien                         extraDataAsJSON:(nullable NSString *)extraDataAsJSON
555d8188f5SBen Roth{
5696c67d7eSEvan Bacon  // In RN 0.8 this was used to invoke the native red box errors (via `showErrorMessage`).
5796c67d7eSEvan Bacon  // The invocation has since been moved into the method that invokes this delegate method.
5896c67d7eSEvan Bacon  // https://github.com/facebook/react-native/commit/31b5b0ac010d44afe3e742e85c75a9ef9e72a9e0#diff-35e5d8a9e7e9ea80b2ccfd41b905b703
595d8188f5SBen Roth}
605d8188f5SBen Roth
6171a1d7edSStanisław Chmiela- (void)handleFatalJSExceptionWithMessage:(nullable NSString *)message
6271a1d7edSStanisław Chmiela                                    stack:(nullable NSArray<NSDictionary<NSString *, id> *> *)stack
635d8188f5SBen Roth                              exceptionId:(NSNumber *)exceptionId
64*529650a2SKudo Chien                          extraDataAsJSON:(nullable NSString *)extraDataAsJSON
655d8188f5SBen Roth{
665d8188f5SBen Roth  NSString *description = [@"Unhandled JS Exception: " stringByAppendingString:message];
675d8188f5SBen Roth  NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey: description, RCTJSStackTraceKey: stack };
685d8188f5SBen Roth  NSError *error = [NSError errorWithDomain:RCTErrorDomain code:0 userInfo:errorInfo];
695d8188f5SBen Roth
70167fd314SWill Schurman  [[EXKernel sharedInstance].serviceRegistry.errorRecoveryManager setError:error forScopeKey:_appRecord.scopeKey];
715d8188f5SBen Roth
725d8188f5SBen Roth  if ([self _isProdHome]) {
735d8188f5SBen Roth    RCTFatal(error);
745d8188f5SBen Roth  }
755d8188f5SBen Roth}
765d8188f5SBen Roth
7771a1d7edSStanisław Chmiela- (void)updateJSExceptionWithMessage:(nullable NSString *)message
7871a1d7edSStanisław Chmiela                               stack:(nullable NSArray *)stack
795d8188f5SBen Roth                         exceptionId:(NSNumber *)exceptionId
805d8188f5SBen Roth{
815d8188f5SBen Roth  [[self _bridgeForRecord].redBox updateErrorMessage:message withStack:stack];
825d8188f5SBen Roth}
835d8188f5SBen Roth
845d8188f5SBen Roth#pragma mark - internal
855d8188f5SBen Roth
865d8188f5SBen Roth- (RCTBridge *)_bridgeForRecord
875d8188f5SBen Roth{
885d8188f5SBen Roth  return _appRecord.appManager.reactBridge;
895d8188f5SBen Roth}
905d8188f5SBen Roth
915d8188f5SBen Roth- (BOOL)_isProdHome
925d8188f5SBen Roth{
935d8188f5SBen Roth  if (RCT_DEBUG) {
945d8188f5SBen Roth    return NO;
955d8188f5SBen Roth  }
965d8188f5SBen Roth  return (_appRecord && _appRecord == [EXKernel sharedInstance].appRegistry.homeAppRecord);
975d8188f5SBen Roth}
985d8188f5SBen Roth
995d8188f5SBen Roth@end
1005d8188f5SBen Roth
1015d8188f5SBen RothNS_ASSUME_NONNULL_END
102