Objective C - Catch exception raised from inside a block - ios

I'm using the following code in app:
#try {
if(!self.usernameField.text || [self.usernameField.text isEqualToString:#""])
[NSException raise:#"Invalid value for username" format:#"Please enter your username."];
if(!self.passwordField.text || [self.passwordField.text isEqualToString:#""])
[NSException raise:#"Invalid value for password" format:#"Please enter your password."];
[LoginManager
userLogin:self.usernameField.text
andPassword:self.passwordField.text
success:^(AFHTTPRequestOperation *op, id response) {
if([self.delegate respondsToSelector:#selector(loginSuccessWithUserName:)]) {
[self.delegate performSelector:#selector(loginSuccessWithUserName:)withObject:self.usernameField.text];
}
[self dismissPopoverController];
}
failure:^(AFHTTPRequestOperation *op, NSError *err) {
NSString* nsLocalizedRecoverySuggestion = [err.userInfo objectForKey:#"NSLocalizedRecoverySuggestion"];
if(err.code == -1009) {
[NSException raise:#"No Internet connection" format:#"It appears you’re not connected to the internet, please configure connectivity."];
}
if([nsLocalizedRecoverySuggestion rangeOfString:#"Wrong username or password."].location != NSNotFound) {
[NSException raise:#"Invalid username or password" format:#"Your given username or password is incorrect"];
}
else {
[NSException raise:#"BSXLoginViewController" format:#"Error during login"];
}
}];
}
#catch (NSException *exception) {
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Login error"
message:exception.description
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
However the exceptions raised in the failure block don't get catched in the catch section.
I kind of understand why it's logical, but i would like to know if there's a way to tell the block that the exceptions happening inside should be handled by the catch section i created.
Thanks for any help!
Sincerely,
Zoli

Don't do this. First of all, and I'm sure you'll get at least a comment from #bbum about this, NSException isn't intended in Objective-C for recoverable errors and propagating recoverable errors through code (see Introduction to Exception Programming Topics for Cocoa). Instead, the construct used in Objective-C is to use NSException basically for unrecoverable programming errors, and to use NSError objects for handing around recoverable errors.
However, you've got a bigger problem here in that the calls that you are making have block callbacks because they return before completing. In this case, your exception handler is exited long before the exceptions are actually thrown.
In this case, I'd suggest removing the exceptions and handling the errors inside of the actual failure: block by dispatching to the main queue and presenting the UIAlert there.

Don't be fooled by the fact that your block is specified inline with other code. You cannot catch exceptions in the block in the outer (non-block) code because the code in the TRY block has already executed (and exited) and the block only executes in it's own scope.
A solution is to catch exceptions in the block.

Related

Block not getting called from requestAttributionDetailsWithBlock

I'm desperate. I'm trying to use iAd's requestAttributionDetailsWithBlock, and it's not calling anything inside my completion handler (I'm using breakpoints in XCode and logs. Any suggestions?
[[ADClient sharedClient] requestAttributionDetailsWithBlock:^void (NSDictionary* attributionDetails, NSError* error)
{
if (error == nil)
{
NSLog(#"Attributes found");
}
else
{
NSLog(#"Request search ads attributes failed with error: %#", error.description);
}
}];
So, I figured out the problem. My code was calling ADClient elsewhere (determineAppInstallationAttributionWithCompletionHandler) and for some reason it was blocking my requestAttributionDetailsWithBlock completion handler from executing. After I commented out the code, everything worked fine. Still not exactly sure why it works, but it does!

How to fail a async XCTestCase which crashes the test before calling completionHandler block?

I am doing iOS unit testing using XCTestCase and I am getting a crash due to "unrecognized selector" on async web service handler which returns manipulated NSDictionary by means of completionHandler block.
The crash is not my expectation for assertion statement (XCTAssertNoThrows) and i want to see the failure for this assertion due to crash but the test method which i have written is stopped execution and ends without any test result (Pass/Fail).
- (void)testMethod
{
XCTestExpectation *expectation = [self expectationWithDescription:#"Task longer than timeout"];
[serviceHandler getInfoCompletionHandler:^(NSDictionary *infoDict){
NSString *value = infoDict[#"Key"];
XCTAssert(value.length > 0, #"Value should not be empty.");
[expectation fulfill];
} failure:^(NSError *error){
NSLog(#"Error: %#",error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error){
if (error) {
NSLog(#"Error: %#",error);
}
}];
}
Unfortunately, the service handler code is not isolated to do proper unit testing. Since it is not isolated, #try...#catch is not catching NSInvalidArgumentException and it is useless. Mostly likely, i want to implement top level NSSetUncaughtExceptionHandler, but i don't know how to fail the associated test from NSSetUncaughtExceptionHandler.

How to use try catch in iOS?

I wanna check the UITextField is String or number by using try catch.
But it seems doesn't work. Here is my code:
- (IBAction)add {
#try {
double firstNumber = self.firstNumber.text.doubleValue;
double secondNumber = self.secondNumber.text.doubleValue;
Calculator *calcu = [[Calculator alloc] initWithFirstNumber:firstNumber andSecondNumber:secondNumber andOperation:#"+"];
self.result.text = calcu.calculateTwoNumber;
}
#catch (NSException *exception) {
self.result.text = #"Please enter right number!";
}
#finally {
}
}
Follow code:
#try {
self.labelName.text=[arrName ObjectAtIndex:indexPath.row];
}
#catch (NSException *exception) {
NSLog(#"Something missing...");
}
#finally {
}
What Claus Bönnhoff wrote in the comments cannot be overemphasized: Objective-C is not Java or C#.
The accepted way to use exceptions in Obj-C is for unrecoverable errors, not simple flow control.
The answer to your question, then, is that exceptions should be used very rarely. In several years of iOS development, I haven't used them a single time. I also have not come accross them in anyone else's code.
I have, however, encountered exceptions thrown by the Obj-C runtime, which I suppose gives some indication of what their role might be.

Uncaught NSException displayed in UIAlertView

I've implemented the following exception handler using:
NSSetUncaughtExceptionHandler(&ExceptionHandler)
The handler then makes a call to handleException using the following:
[[AppContext alloc] init] performSelectorOnMainThread:#selector(handleException:) withObject:exception waitUntilDone:YES];
Which has been implemented as such:
NSString *message = [NSString stringWithFormat:MSG_UNHANDLED_EXCEPTION_FORMAT, exception.reason, [exception callStackSymbols]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:TITLE_UNHANDLED_EXCEPTION message:message delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[alert show];
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!_quit) {
for (NSString *mode in (__bridge NSArray *)allModes) {
CFRunLoopRunInMode((__bridge CFStringRef)mode, 0.001, false);
}
}
CFRelease(allModes);
This works well except for the following scenario. I still get the UIAlertView to show but the user interaction is no longer available (user can't scroll message or tap OK).
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){
// do long work on another thread...
dispatch_async(dispatch_get_main_queue(), ^(){
// updating the UI so get back on main thread...
// exception occurs in this block (example)...
#throw [NSException exceptionWithName:#"DispatchAsyncToMainThreadException" reason:#"Example" userInfo:nil];
});
Note: I am not actually throwing an exception in my production code. This is purely to show where the unhandled exception is being generated.
What am I missing that should be accounted for when the exception is being raised from within a block that has been dispatched back to the main queue.
The documentation for NSSetUncaughtExceptionHandler states that you should be doing last-minute error logging before the application terminates. You should not expect any UI to work at this point and should only be logging somewhere to the file system the details of the error. When the user opens the application again you can let them know something went wrong and possible offer recovery/reporting options.
Discussion Sets the top-level error-handling function where you can perform last-minute logging before the program terminates.

NSFileHandle writeData: exception handling

This could be rather broad problem but I could not find any online resource addressing or explaining this matter.
The question is after creating NSFileHandle *writer = [NSFileHandle fileHandleForWritingAtPath:"path"] and when you use [writer writedata:"NSData"] there are sine possible exception that could fire according to the apple doc.
"This method raises an exception if the file descriptor is closed or is
not valid, if the receiver represents an unconnected pipe or socket
endpoint, if no free space is left on the file system, or if any other
writing error occurs." - APPLE DOC
All I want to know is is there any way we can handle or validate these issues without using any try catch or checking for each error in a condition check before write. Any possible way we can use NSError to handle this ?
I would say "No". If you did manage to find a test that covered all possible failures before writing, then there is nothing to say that the write operation might fail after this initial test (think about writing to a filesystem with 1KB free and you want to write 4KB).
Therefore wrapping your calls to these methods inside a #try/#catch block would seem to me to be the best approach. These wrappers could then return an NSError ** if you want details of the failure (which you most certainly should want).
- (BOOL)writeData:(NSData *)data
toFileHandle:(NSFileHandle *)fileHandler
error:(NSError **)error
{
#try
{
[fileHandler writeData:data];
}
#catch (NSException *e)
{
if (error != NULL)
{
NSDictionary *userInfo = #{
NSLocalizedDescriptionKey : #"Failed to write data",
// Other stuff?
};
*error = [NSError errorWithDomain:#"MyStuff" code:123 userInfo:userInfo];
}
return NO;
}
return YES;
}
You will certainly want to get the reason for the failure into the NSError, but it's not immediately obvious to me how to go about doing this.

Resources