Skip to content

Commit c144bbf

Browse files
neilsarkarFacebook Github Bot
authored andcommitted
Allow serializing underlying NSError objects, closes facebook#10506
Summary: Explain the **motivation** for making this change. What existing problem does the pull request solve? See facebook#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 facebook#10507 Differential Revision: D4080802 Pulled By: lacker fbshipit-source-id: 93a41d9e9a710e406a6ccac214a5617271b4bede
1 parent f645389 commit c144bbf

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ - (void)testEncodingString
4242
XCTAssertEqualObjects(json, RCTJSONStringify(text, NULL));
4343
}
4444

45+
- (void)testEncodingNSError
46+
{
47+
NSError *underlyingError = [NSError errorWithDomain:@"underlyingDomain" code:421 userInfo:nil];
48+
NSError *err = [NSError errorWithDomain:@"domain" code:68 userInfo:@{@"NSUnderlyingError": underlyingError}];
49+
50+
// An assertion on the full object would be too brittle since it contains an iOS stack trace
51+
// so we are relying on the behavior of RCTJSONParse, which is tested below.
52+
NSDictionary<NSString *, id> *jsonObject = RCTJSErrorFromNSError(err);
53+
NSString *jsonString = RCTJSONStringify(jsonObject, NULL);
54+
NSDictionary<NSString *, id> *json = RCTJSONParse(jsonString, NULL);
55+
XCTAssertEqualObjects(json[@"code"], @"EDOMAIN68");
56+
XCTAssertEqualObjects(json[@"message"], @"The operation couldn’t be completed. (domain error 68.)");
57+
XCTAssertEqualObjects(json[@"domain"], @"domain");
58+
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"code"], @"421");
59+
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"message"], @"underlying error");
60+
XCTAssertEqualObjects(json[@"userInfo"][@"NSUnderlyingError"][@"domain"], @"underlyingDomain");
61+
}
62+
63+
4564
- (void)testDecodingObject
4665
{
4766
NSDictionary<NSString *, id> *obj = @{@"foo": @"bar"};

React/Base/RCTUtils.m

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,18 +413,28 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
413413
{
414414
NSString *errorMessage;
415415
NSArray<NSString *> *stackTrace = [NSThread callStackSymbols];
416+
NSMutableDictionary *userInfo;
416417
NSMutableDictionary<NSString *, id> *errorInfo =
417418
[NSMutableDictionary dictionaryWithObject:stackTrace forKey:@"nativeStackIOS"];
418419

419420
if (error) {
420421
errorMessage = error.localizedDescription ?: @"Unknown error from a native module";
421422
errorInfo[@"domain"] = error.domain ?: RCTErrorDomain;
423+
if (error.userInfo) {
424+
userInfo = [error.userInfo mutableCopy];
425+
if (userInfo != nil && userInfo[NSUnderlyingErrorKey] != nil) {
426+
NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
427+
NSString *underlyingCode = [NSString stringWithFormat:@"%d", (int)underlyingError.code];
428+
userInfo[NSUnderlyingErrorKey] = RCTJSErrorFromCodeMessageAndNSError(underlyingCode, @"underlying error", underlyingError);
429+
}
430+
}
422431
} else {
423432
errorMessage = @"Unknown error from a native module";
424433
errorInfo[@"domain"] = RCTErrorDomain;
434+
userInfo = nil;
425435
}
426436
errorInfo[@"code"] = code ?: RCTErrorUnspecified;
427-
errorInfo[@"userInfo"] = RCTNullIfNil(error.userInfo);
437+
errorInfo[@"userInfo"] = RCTNullIfNil(userInfo);
428438

429439
// Allow for explicit overriding of the error message
430440
errorMessage = message ?: errorMessage;

0 commit comments

Comments
 (0)