Permalink
Browse files

Allow serializing underlying NSError objects, closes #10506

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

See #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 #10507

Differential Revision: D4080802

Pulled By: lacker

fbshipit-source-id: 93a41d9e9a710e406a6ccac214a5617271b4bede
  • Loading branch information...
neilsarkar authored and Facebook Github Bot committed Oct 26, 2016
1 parent f645389 commit c144bbfb7ebdd174707638eaa344a9fddb38f253
Showing with 30 additions and 1 deletion.
  1. +19 −0 Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m
  2. +11 −1 React/Base/RCTUtils.m
@@ -42,6 +42,25 @@ - (void)testEncodingString
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 couldn’t 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
@@ -413,18 +413,28 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
{
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;

0 comments on commit c144bbf

Please sign in to comment.