Allow serializing underlying NSError objects, closes #10506

Summary:
Explain the **motivation** for making this change. What existing problem does the pull request solve?

See https://github.com/facebook/react-native/issues/10506. A native `NSError` with `NSUnderlyingErrorKey` set causes a JSON stringify error from the websocket dispatcher if remote debugging is enabled.

**Test plan (required)**

I'm not familiar with the react native testing framework. Happy to add a test for this if someone can point me to where this part of the codebase is exercised :)

I did some spot checks with nil user dictionaries and nil underlying errors here. The case that this solves is testable using https://github.com/superseriouscompany/react-native-error-repro, specifically:

```objective-c
NSError *underlyingError = [NSError errorWithDomain:@"underlyingDomain" code:421 userInfo:nil];
NSError *err = [NSError errorWithDomain:@"domain" code:68 userInfo:@{@"NSUnderlyingError": underlyingError}];

reject(@"foo", @"bar", err);
```
Closes https://github.com/facebook/react-native/pull/10507

Differential Revision: D4080802

Pulled By: lacker

fbshipit-source-id: 93a41d9e9a710e406a6ccac214a5617271b4bede
This commit is contained in:
Neil Sarkar 2016-10-26 01:40:24 -07:00 committed by Facebook Github Bot
parent f64538943e
commit c144bbfb7e
2 changed files with 30 additions and 1 deletions

View File

@ -42,6 +42,25 @@
XCTAssertEqualObjects(json, RCTJSONStringify(text, NULL));
}
- (void)testEncodingNSError
{
NSError *underlyingError = [NSError errorWithDomain:@"underlyingDomain" code:421 userInfo:nil];
NSError *err = [NSError errorWithDomain:@"domain" code:68 userInfo:@{@"NSUnderlyingError": underlyingError}];
// An assertion on the full object would be too brittle since it contains an iOS stack trace
// so we are relying on the behavior of RCTJSONParse, which is tested below.
NSDictionary<NSString *, id> *jsonObject = RCTJSErrorFromNSError(err);
NSString *jsonString = RCTJSONStringify(jsonObject, NULL);
NSDictionary<NSString *, id> *json = RCTJSONParse(jsonString, NULL);
XCTAssertEqualObjects(json[@"code"], @"EDOMAIN68");
XCTAssertEqualObjects(json[@"message"], @"The operation couldnt be completed. (domain error 68.)");
XCTAssertEqualObjects(json[@"domain"], @"domain");
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"code"], @"421");
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"message"], @"underlying error");
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"domain"], @"underlyingDomain");
}
- (void)testDecodingObject
{
NSDictionary<NSString *, id> *obj = @{@"foo": @"bar"};

View File

@ -413,18 +413,28 @@ NSDictionary<NSString *, id> *RCTJSErrorFromCodeMessageAndNSError(NSString *code
{
NSString *errorMessage;
NSArray<NSString *> *stackTrace = [NSThread callStackSymbols];
NSMutableDictionary *userInfo;
NSMutableDictionary<NSString *, id> *errorInfo =
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
if (error) {
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
if (error.userInfo) {
userInfo = [error.userInfo mutableCopy];
if (userInfo != nil && userInfo[NSUnderlyingErrorKey] != nil) {
NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
NSString *underlyingCode = [NSString stringWithFormat:@"%d", (int)underlyingError.code];
userInfo[NSUnderlyingErrorKey] = RCTJSErrorFromCodeMessageAndNSError(underlyingCode, @"underlying error", underlyingError);
}
}
} else {
errorMessage = @"Unknown error from a native module";
errorInfo[@"domain"] = RCTErrorDomain;
userInfo = nil;
}
errorInfo[@"code"] = code ?: RCTErrorUnspecified;
errorInfo[@"userInfo"] = RCTNullIfNil(error.userInfo);
errorInfo[@"userInfo"] = RCTNullIfNil(userInfo);
// Allow for explicit overriding of the error message
errorMessage = message ?: errorMessage;