In advance, please excuse my lack of understanding for iPhone/Objective-C Best Practices; I come from a .NET/C# background.
Although, I've read many posts regarding exception handling for the iPhone, I'm still not exactly clear on what most people do for production code. Also, I haven't been able to find any open source apps with error handling that I would normally expect. Here are my questions:
1) If an unexpected result occurs that would cause the application to eventually fail, would you throw an exception or just wait for it to fail later? For example,
if (![fileManager createDirectoryAtPath: myNewDir
withIntermediateDirectories: YES
attributes: nil
error: &myError]) {
// My app shouldn't continue. Should I raise an exception?
// Or should I just log it and then wait for it to crash later?
}
2) Do you validate parameters? For example, in C# I would usually check for null, and if needed throw an ArgumentNullException.
3) When an app crashes, does the crash info get logged automatically or do I need to set the unhandled exception handler? Can I show a UIAlertView within this method to inform the user something bad happened, instead of having the app just disappear? (If so, I couldn't get it to work.)
4) And finally, why don't I see anyone actually using #try/#catch/#finally? It's used extensively in C#, but I haven't found open source apps using it. (Maybe I'm just looking at the wrong apps.)
Ad 1. The example you give is a somewhat classical example of when one should use a "checked exception" in Java. The caller of the method has to be prepared, that the call can fail for reasons which are outside of what it can control. In particular, in the example given, I would allow the error descriptor object allow to propagate back to the caller:
- (BOOL) prepareDirectories: (NSString*) path error: (NSError**) location {
if( ![fileManager createDirectoryAtPath: path
withIntermediateDirectories: YES
attributes: nil
error: location] ) {
return NO;
}
// ... additional set-up here
return YES;
}
If the error is really not recoverable, you may actually raise an exception, but that will also forfeit any chance to show a proper error message to the user of your application.
Ad 2. Yes, parameter validation is definitely something you should do. And raising an exception for unexpected nils is entirely appropriate. A bad parameter is a "programmer's error" and you cannot guarantee, that the program is still in a consistent state. For example, NSArray raises an exception, if you try to get an element using an non-existent index.
Ad 3. When the app crashes due to an uncaught exception, the fact is logged automatically. You can catch the exception if you really want to display an error message. However, the app is likely to be in an inconsistent state, and should not be allowed to continue, so simply allowing it to go down seems the best solution here.
Ad 4. I don't know.
In the specific case you mention, you should present a notification to the user about what just happened with instructions on how to fix the situation. Except in extreme circumstances your app should not quit. You should let the user fix whatever is wrong, then try again.
Parameter validation depends on a lot of factors. One thing to keep in mind is that in Obj-C it's perfectly valid to send messages to nil (nothing happens if you do) so in a lot of situations you don't need to check for nil. If I'm in a model class I always validate parameters in methods. If I'm not I almost never validate anything, type checking is good enough.
Some information should be logged but it's always helpful to log more specific information to your situation. Ideally you shouldn't ever reach the unhandled exception state.
Cocoa classes will rarely throw exceptions for environmental reasons, they are almost always because you did something wrong. For example, you wouldn't usually put a #try/#catch block around [NSString stringWithString:] because the only way it'll throw an exception is if you try and pass nil. Make sure you aren't passing nil and you won't have to worry about catching exceptions. When a method might fail because of something outside of your control, they usually communicate via an NSError. See the - (BOOL)save:(NSError **)error method of NSManagedObjectContext for example.
Related
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
}
With in this method one could arrest the error that happened for ex - ("Failed to Connect to iTunes") etc. Now if one wants to show this error message as an alert it would be really helpful for users. Like one example is when the "Failed to Connect to iTunes" error is shown as an UIAlertView, it can lead the user to the problem that his wi-fi was off all this while. He can rectify this problem and retry. But before one chooses to display these errors as UIAlertView, one must know all the possible error values that are returned because a user won't be pleased to see something like "Error %^#)$()(!)#()+! code 123_123443 blah"i.e.something incomprehensible to him. How to best handle this ? As an after thought, it would be nice to know all the possible error messages of NSError thrown in this method.
Thanks in advance
one must know all the possible error
values that are returned because a
user won't be pleased to see something
like "Error %^#)$()(!)#()+! code
123_123443 blah"i.e.something
incomprehensible to him
I don't know all the possible errors that particular method can return but I think it is safe enough to use the localizedDescription method of NSError to present the error to the user.
From Apple docs:
Error objects in the Cocoa domain are
always localized and ready to present
to users, so they can often be
presented without further evaluation.
When I generate a dump file using ADPlus, I get both First chance and second chance exception but when I use task manager for generating dump file, I only get once dump file. Is it the second chance exception? I am bit confused about this 1st and 2nd chance exception anyways even though i have read a little bit about it. May be if someone can provide some good analogy, that might clear up things for me
See here: Link
In short, First chance exception gives the debugger a first chance to inspect the exception and application state before the application handles the exception.
You can stop the debugger at this point (it' usually a setting like "break into debugger when exception is created". Often this is off by default). If you don't, or if you let the application continue to run, the exception is passed on to the application.
The debugger gets a second chance at the exception when the application doesn't handle it. Again, you can break into the debugger here (this isusually on by default).
Note that if the application doesn't handle the exception, the application will usually terminate.
Is there anyway to implement a global exception handler for iPhone apps such that exceptions, instead of silently crashing the app, could allow for some sort of message?
I can understand if it's not do-able since the program may be in an inconsistent state, but it'd be nice to at least tell users "Sorry - something went wrong!"
Thanks!
Check this question for the answer. It seems to indicate that you'll be getting junky stack traces, but you definitely can set a global exception handler. Good luck!
So, I have a basic app for storing, searching, and manipulating data. Basic CRUD operations. In various places of my code where I'm storing or updating this data, I basically have this:
NSError *error;
if (![self.managedObjectContext save:&error]) {
// TODO: Handle this error
NSLog(#"Error while saving data %#, %#", error, [error userInfo]);
}
What do most people do, user-experience-wise, when these sorts of things come up?
The only thing that comes to mind is to just pop up some horrible UIAlertView with a vague message that something went wrong; not really sure how to recover these things.
For the sake of argument, lets assume my model has little to no validations, so the only errors that might occur would either be something horribly wrong or a programming problem.
Any good ideas on the user experience?
Jakob Nielsen has a few concise guidelines you might want to check out; out of everything he proposes, the one I strongly suggest you indicate in an error message that it's not the user's fault. From my own experience in user testing, most users believe they did something wrong when an error pops up, and this leads to frustration.
Maybe something like:
There was a problem saving: don't worry it's not your fault! If you restart the app, you can try again. But please contact [developer contact] and tell him the error was [short, memorable error code]
In a case like this, I think the best thing to do would be to present an alert to the user and quit the app.
Short answer is that I have never had a save error occur in production. Normally I make these asserts and have them crash the app. This is to make sure I catch them during development.
If your application is well-written you should not have one of these occur when a user is running the application.
As for the text, it depends on your application and there is no general rule for them.
We are writing an API for iphone developers and we don't know what the best practice is for exception handling. We looked into NSError, standard POSIX way, NSException
What is the convention that most APIs use? Which is the most "Objective-C friendly"?
From the Introduction to Exception Programming Topics:
Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
...
Instead of exceptions, error objects (NSError) and the Cocoa error-delivery mechanism are the recommended way to communicate expected errors in Cocoa applications. For further information, see Error Handling Programming Guide For Cocoa.
So as I understand it, only use exceptions when something is fatally wrong. Otherwise, use NSError objects.
+1 for NSError.
I forget where in the Apple docs I read this, but I also recall them encouraging the coding philosophy of "try first, then check for errors," as opposed to "check for validity, then do the operation." For example, instead of seeing if the network is available before using it, just try to use it and respond to an error if/when you get one back.
I agree with this philosophy for many use cases because (a) it moves validity-checking to the moment of action, so in a sense it's more accurate, and (b, subjective) it's more fun to work with code in this pattern.
To summarize, the suggestion is to use NSError, and to provide immediate feedback with NSError** parameters that accept NULL, to be very friendly to your API-users! This pattern is also established in several places in Cocoa/Touch; for example the NSString method writeToFile:atomically:encoding:error:.