Skip to content

Commit

Permalink
Standardize Error objects for Promises
Browse files Browse the repository at this point in the history
Summary:
public

Promises are coming.  And as part of it, we are standardizing the error objects that will be returned.  This puts the code in place on the Android side to always send the proper error format.

It will be an error object like this
  {
    code : "E_SOME_ERROR_CODE_DEFINED_BY_MODULE", // Meant to be machine parseable
    message : "Human readable message",
    nativeError : {} // Some representation of the underlying error (Exception or NSError) , still figuring out exactly, but hopefully something with stack info
  }

Reviewed By: nicklockwood

Differential Revision: D2840128

fb-gh-sync-id: 174d620e2beb53e1fc14161a10fd0479218d98a6
  • Loading branch information
Dave Miller authored and facebook-github-bot-5 committed Jan 19, 2016
1 parent 554292d commit 0c5f279
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Libraries/RCTTest/RCTTestModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ - (dispatch_queue_t)methodQueue

RCT_REMAP_METHOD(shouldReject, shouldReject_resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
reject(nil);
reject(nil, nil, nil);
}

RCT_EXPORT_METHOD(markTestCompleted)
Expand Down
2 changes: 1 addition & 1 deletion React/Base/RCTBridgeModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ typedef void (^RCTPromiseResolveBlock)(id result);
* The error may be nil but it is preferable to pass an NSError object for more
* precise error messages.
*/
typedef void (^RCTPromiseRejectBlock)(NSError *error);
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);

/**
* This constant can be returned from +methodQueue to force module
Expand Down
4 changes: 2 additions & 2 deletions React/Base/RCTModuleMethod.m
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ - (void)processMethodSignature
return NO;
}

RCT_BLOCK_ARGUMENT(^(NSError *error) {
NSDictionary *errorJSON = RCTJSErrorFromNSError(error);
RCT_BLOCK_ARGUMENT(^(NSString *code, NSString *message, NSError *error) {
NSDictionary *errorJSON = RCTJSErrorFromCodeMessageAndNSError(code, message, error);
[bridge enqueueCallback:json args:@[errorJSON]];
});
)
Expand Down
4 changes: 4 additions & 0 deletions React/Base/RCTUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ RCT_EXTERN BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);
RCT_EXTERN NSDictionary<NSString *, id> *RCTMakeError(NSString *message, id toStringify, NSDictionary<NSString *, id> *extraData);
RCT_EXTERN NSDictionary<NSString *, id> *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary<NSString *, id> *extraData);
RCT_EXTERN NSDictionary<NSString *, id> *RCTJSErrorFromNSError(NSError *error);
RCT_EXTERN NSDictionary<NSString *, id> *RCTJSErrorFromCodeMessageAndNSError(NSString *code, NSString *message, NSError *error);

// The default error code that will be sent in the .code value of the Error object to js
RCT_EXTERN NSString *const RCTErrorUnspecified;

// Returns YES if React is running in a test environment
RCT_EXTERN BOOL RCTRunningInTestEnvironment(void);
Expand Down
17 changes: 13 additions & 4 deletions React/Base/RCTUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#import "RCTLog.h"

NSString *const RCTErrorUnspecified = @"EUNSPECIFIED";

NSString *RCTJSONStringify(id jsonObject, NSError **error)
{
static SEL JSONKitSelector = NULL;
Expand Down Expand Up @@ -313,27 +315,34 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
return error;
}

// TODO: Can we just replace RCTMakeError with this function instead?
NSDictionary<NSString *, id> *RCTJSErrorFromNSError(NSError *error)
{
return RCTJSErrorFromCodeMessageAndNSError(RCTErrorUnspecified, nil, error);
}

// TODO: Can we just replace RCTMakeError with this function instead?
NSDictionary<NSString *, id> *RCTJSErrorFromCodeMessageAndNSError(NSString *code, NSString *message, NSError *error)
{
NSString *errorMessage;
NSArray<NSString *> *stackTrace = [NSThread callStackSymbols];
NSMutableDictionary<NSString *, id> *errorInfo =
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];

if (error) {
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
errorInfo[@"code"] = @(error.code);
} else {
errorMessage = @"Unknown error from a native module";
errorInfo[@"domain"] = RCTErrorDomain;
errorInfo[@"code"] = @-1;
}
errorInfo[@"code"] = code ?: RCTErrorUnspecified;
// Allow for explicit overriding of the error message
errorMessage = message ?: errorMessage;

return RCTMakeError(errorMessage, nil, errorInfo);
}


BOOL RCTRunningInTestEnvironment(void)
{
static BOOL isTestEnvironment = NO;
Expand Down

2 comments on commit 0c5f279

@philikon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that RCTPromiseRejectBlock needs different info and therefore gets a different signature than RCTResponseErrorBlock. Either way, https://facebook.github.io/react-native/docs/native-modules-ios.html#promises probably wants to be updated :)

@satya164
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great if you could send a PR @philikon :)

Please sign in to comment.