JRErr icon indicating copy to clipboard operation
JRErr copied to clipboard

Creating a custom error?

Open nriley opened this issue 11 years ago • 2 comments

If I want to throw a JRErrException with additional information (e.g., recovery options) I can't figure out how to do this cleanly with JRErr.

The only solution I've been able to find is:

NSError *error = […];
JRThrowErr((*jrErrRef = error, NO));

which is just horrendously ugly.

It looks like calling JRErrReportError should be the right path, but I don’t have an expression to use with it. Am I missing something?

nriley avatar Aug 18 '13 03:08 nriley

It's well within JRErr's scope to handle custom errors. If it doesn't handle your specific case then I may need to extend it.

Do you want to support an error class that has a custom type or do you just want to add additional information to a generic error (say a BOOL)?

If the former, check out the unit test code that converts a WeirdError into an NSError: https://github.com/rentzsch/JRErr/blob/d39777f090d24ad20b4e063784a6b9161e41d5a7/JRErrTests/JRErrTest.m#L35. I recommend this route if at all possible since it automates so much.

If the latter, you can build your own NSError the manual way (preferably with the help of JRMakeErrUserInfo() to fill in the all-important origin information (perhaps which needs enhancement to ease inclusion of recovery options and other keys)). Let's call this myError.

Since you're building the error manually, there's no associated expression (the line of code that originated the error). Additionally, you're not trying to detect whether an error is present -- you know. At that point you just want to push the error onto the thread-local error stack and throw it manually:

NSError *myError = [NSError errorWithDomain:@"MyDomain" code:-128 userInfo:JRMakeErrUserInfo()];
[[JRErrContext currentContext] pushError:myError];
@throw [JRErrException exceptionWithError:jrErr];

The last two lines are tedious if you're planning on doing it a lot. I'm open to adding a macro to ease the burden.

rentzsch avatar Aug 18 '13 06:08 rentzsch

Yes, it is the last 2 lines (3 if you consider the JRMakeErrUserInfo) that I was referring to. This is (specifically) what I'm doing now:

NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                          description,        NSLocalizedDescriptionKey,
                          recoverySuggestion, NSLocalizedRecoverySuggestionErrorKey,
                          recoveryOptions,    NSLocalizedRecoveryOptionsErrorKey,
                          self,               NSRecoveryAttempterErrorKey,
                          nil];

NSError *error = [NSError errorWithDomain: [[self class] description]
                                     code: PSAlarmAlertsFailedToDeserializeError
                                 userInfo: userInfo];

JRThrowErr((*jrErrRef = error, NO)); // XXX a better way?

While ugly and inefficient, this does do what I want — it merges JRErr's user info into mine and throws the exception.

I need to check that the error is the one I'm expecting later during recovery so I can't use JRErrDomain/–1; this logic is already in JRErrReportError. The domain is automatically generated in JRMakeErrMsg as I do above, but it's probably too much magic to put it in the macro.

No idea what to call this macro though. JRThrowErr seems like it'd actually be a good name, but it's already used. (Something like JRThrowOnErr or JROnErrThrow would have made more sense to me, FWIW.) JRThrowError would be consistent but confusing. Maybe you have a better idea?

nriley avatar Aug 18 '13 17:08 nriley