Catching NSException in main - ios

The iOS application i took over has a way of handling NSExceptions differently than what i've seen before and wanted to know why it isn't working now.
In the main.m file the old developer has this logic in it:
int main(int argc, char *argv[])
{
#autoreleasepool {
int retVal = 0;
#try {
retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
#catch (NSException *exception) {
//
//Logic to save Exception in DataStore
//
NSLog(#"Exception - %#",[exception description]);
exit(EXIT_FAILURE);
}
return retVal;
}
}
When the app would get launched again you would receive a prompt to send the exception to us and if you confirmed it would sent it to our server.
I recently pushed an update more optimized for iOS 7 of the application and noticed that i don't get any of these error reports from crashed apps anymore.
So i tested it via the following code which i know gets called:
NSArray *array = [NSArray new];
id object = [array objectAtIndex:4];
To which i receive this:
2014-05-12 14:55:57.575 APPNAME[17989:60b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 4 beyond bounds for empty array'
*** First throw call stack:
(0x18ab0b09c 0x196a89d78 0x18aa0c680 0x10007f498 0x18d9e02c0 0x18da997d0 0x18da996cc 0x18da98ba0
0x18da98840 0x18da98564 0x18da984e4 0x18d9dad78 0x18d5d70cc 0x18d5d1c94 0x18d5d1b4c
0x18d5d13d4 0x18d5d1178 0x18d5caa30 0x18aacb7e0 0x18aac8a68 0x18aac8df4 0x18aa09b38
0x19042f830 0x18da480e8 0x100036b98 0x197073aa0)
libc++abi.dylib: terminating with uncaught exception of type NSException
As you can see the exception is not being logged or saved but rather it is completely uncaught.
2 Questions:
Is this bad practice? I don't think it is but i am a jr. developer and not really sure and if there is a better way to do this w/o third party service?
Do you know of any changes in iOS 7 that would affect this (UNTOUCHED) Logic?

Is this bad practice? I don't think it is but i am a jr. developer and
not really sure and if there is a better way to do this w/o third
party service?
Yes, it is bad practice. UIApplicationMain() is a black hole; once control is passed to that function, it is unlikely that any code beyond that call in main() will ever be invoked again.
It is also a naive implementation; it logs less information to stdout than the normal exception handling mechanism (which is the output you are seeing). It hides information that would be useful to debugging and would also, likely, bypass the standard crash reporting mechanism.
Note that there is also a global unhandled exception handling hook. I wouldn't recommend using it, but it does exist.
Do you know of any changes in iOS 7 that would affect this (UNTOUCHED)
Logic?
Not off hand, but I haven't looked. Again, though, UIApplicationMain() is a black hole; it doesn't typically return ever.

According to Exception Programming Topics you can use NSSetUncaughtExceptionHandler function to handle uncaught exceptions. Try the code below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(handleUncaughtException);
#throw [NSException exceptionWithName:NSGenericException
reason:#"Test uncaught exception handling"
userInfo:nil];
return YES;
}
void handleUncaughtException(NSException *exception)
{
NSLog(#"Exception - %#",[exception description]);
exit(EXIT_FAILURE);
}

Related

iOS - Catching exceptions in main()

So, I had an idea to catch unanticipated exceptions in main and try to cleanup and exit gracefully:
int main(int argc, char *argv[])
{
#autoreleasepool
{
//return UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
#try
{
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
return retVal;
}
#catch (NSException *exception)
{
[Utilities setPreferencesDefaults];
}
}
}
This does catch exceptions and updates the preference defaults.
I then thought, why exit at all, just cleanup and relaunch, so I wrapped everything in a while loop:
int main(int argc, char *argv[])
{
while (YES)
{
#autoreleasepool
{
...
}
}
}
Of course I wouldn't be here if that actually worked. Problem is, once it again executes
retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
it immediately throws a new exception:
Assertion failure in void UIApplicationInstantiateSingleton(Class)(), /SourceCache/UIKit/UIKit-2380.17/UIApplication.m:2037
NSInternalInconsistencyException
#"There can only be one UIApplication instance."
Makes sense, so is there a way I can discard the existing singleton and replace it with a new one? (although I guess it's not really a singleton if I can)
Purpose is, I don't ever want the app to crash giving a bad user experience. Even if their state isn't completely restored, I would think that would still be better than just unexpectedly exiting.
I can try to handle possible expected exceptions, but this is to try to catch things that I haven't foreseen.
This should really only catch VERY unusual circumstances, so if it can't be done it's not that big of a deal, but I was wondering how best to deal with this type of situation.
That won't work because the exception mechanism does not cleanup properly when thrown across stack frames. Since you are catching the exception in main the exception has crossed several stack frames.
Apple explicitly states that exceptions are only to be used for unrecoverable programming errors.
See SO answer by bbum:
"Any exception that passes through system framework code will leave said framework in an undefined state.. Catching said exceptions and trying to recover from it will lead to memory leaks, undefined behaviour and crashing."
Also by bbum.
From Apple docs:
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.
You can create a top level exception handler and set it as your default exception handler when you app first launches. The best place to do this would be the AppDelegate's applicationDidFinishLaunching: method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
// some UI configuration here..
return YES;
}
void exceptionHandler(NSException *exception)
{
[Utilities setPreferencesDefaults];
// You can also write exception message into a log file as well.
NSLog(#"Stack trace: %#", [exception callStackReturnAddresses]);
}

How to implement test-flight crash reporting in swift

I am developing an app in swift. I integrated test-flight SDK for remote logging. Now I want to use other test-flgiht SDK provided features. One of them is crash reporting. In the test-flight provided sample code c functions are there. How can I implement those c calls
The sample code test-flight provided
/*
My Apps Custom uncaught exception catcher, we do special stuff here, and TestFlight takes care of the rest
*/
void HandleExceptions(NSException *exception) {
NSLog(#"This is where we save the application data during a exception");
// Save application data on crash
}
/*
My Apps Custom signal catcher, we do special stuff here, and TestFlight takes care of the rest
*/
void SignalHandler(int sig) {
NSLog(#"This is where we save the application data during a signal");
// Save application data on crash
}
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// installs HandleExceptions as the Uncaught Exception Handler
NSSetUncaughtExceptionHandler(&HandleExceptions);
// create the signal action structure
struct sigaction newSignalAction;
// initialize the signal action structure
memset(&newSignalAction, 0, sizeof(newSignalAction));
// set SignalHandler as the handler in the signal action structure
newSignalAction.sa_handler = &SignalHandler;
// set SignalHandler as the handlers for SIGABRT, SIGILL and SIGBUS
sigaction(SIGABRT, &newSignalAction, NULL);
sigaction(SIGILL, &newSignalAction, NULL);
sigaction(SIGBUS, &newSignalAction, NULL);
// Call takeOff after install your own unhandled exception and signal handlers
[TestFlight takeOff:#"Insert your Application Token here"];
// continue with your application initialization
}
Link for the above code:http://help.testflightapp.com/customer/portal/articles/829558-how-do-i-implement-crash-reporting-
Any help would be appreciated.

Assertion failure from Cocos2d CCNode.m on iOS device. Fine in simulator. Why?

Can anyone shed any light on the reason for the following failure:
*** Assertion failure in -[Alien rotation],
/Users/admininstrator/Documents/moonweed/Star Pilot v4.0/Star Pilot
#1/Libraries/cocos2d/CCNode.m:258
2014-11-06 23:28:51.680 Star Pilot #1[2141:987748] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'CCNode#rotation. rotationalSkewX
!= rotationalSkewY. Don't know which one to return'
Looking at the Cocos2d source, I can see that the Assertion is coming from CCNode.m, from the method below:
-(float) rotation
{
CCPhysicsBody *body = GetBodyIfRunning(self);
if(body){
return -CC_RADIANS_TO_DEGREES(body.absoluteRadians) +
NodeToPhysicsRotation(self.parent);
} else {
NSAssert( _rotationalSkewX == _rotationalSkewY, #"CCNode#rotation. rotationalSkewX != rotationalSkewY. Don't know which one to return");
return _rotationalSkewX;
}
}
The code runs fine on all simulators, using Xcode 6.1. But on my iPhone 5, it crashes. I'm struggling to figure why? I'm currently using Cocos2d V3.x.

Send a log to Crashlytics without an app crash

How can I get Crashlytics to receive a log without my app crashing?
I have the following code:
if(!context.managedObjectContext save:&error) {
CLS_LOG(#"%#",error.description)
}
When an error occurs, I want the Crashlytics server to receive the error but the app should continue running.
I do not need the log right away. I would be happy to get the log on the next restart. I just do not want to have to trigger a crash in my app to receive the log.
Is this possible?
With the new update from crashlytics you can now use:
[[FIRCrashlytics crashlytics] recordError:error];
And in Swift:
Crashlytics.crashlytics().record(error: error)
You can check the documentation here.
Kinda old question, but now you can use Answers which is part of the Fabric suit (Crashlytics is part of Fabric as well):
Fabric can be found here. And further documentation here.
I tried the below lines and it works like charm. In try-catch block use the below lines in your catch block
#try {
// line of code here
}
#catch (NSException *exception) {
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
handler(exception);
}
as explained at http://support.crashlytics.com/knowledgebase/articles/222764-can-i-use-a-custom-exception-handler
[UPDATE]
Now in fabric's crashlytics we can use simple function [Crashlytics recordCustomExceptionName:reason:frameArray:] for sending handled exceptions
#try {
// line of code here
}
#catch (NSException *exception) {
NSArray *stack = [exception callStackReturnAddresses];
[[Crashlytics sharedInstance] recordCustomExceptionName: exception.name
reason: exception.reason
frameArray: stack];
}
as explained at
https://twittercommunity.com/t/crashlytics-ios-how-to-send-non-fatal-exceptions-without-app-crash/34592/32
For me the method .recordError() didn't helped, because it don't log the user information. Or i just didn't found where to watch it. Using recordCustomExceptionName fit to me. There is an example of implementation of the both ways:
func logMessage(_ message: String) {
let userInfo = ["message" : message]
let error = NSError(domain: "AppErrorDomain", code: 1, userInfo: userInfo)
Crashlytics.sharedInstance().recordCustomExceptionName("API Error", reason: message, frameArray: [])
Crashlytics.sharedInstance().recordError(error, withAdditionalUserInfo: userInfo)
}
Swift 5, Crashlytics SDK 4.0.0-beta.6:
let exceptionModel = ExceptionModel(name: "exception title", reason: "details")
Crashlytics.crashlytics().record(exceptionModel: exceptionModel)
...similar for NSError, with whatever you want to see in the Crashlytics dashboard.
let error = NSError(domain: "error title", code: 0, userInfo: ["message":"some details"])
Crashlytics.crashlytics().record(error: error)
Crashlytics is a crash tracking service, if you need to track custom messages choose other analytics service.
In reference from Crashlytics documents.
try {
myMethodThatThrows();
} catch (Exception e) {
Crashlytics.logException(e);
// handle your exception here!
}
https://docs.fabric.io/android/crashlytics/caught-exceptions.html?caught%20exceptions#caught-exceptions
As far as I know, if you dont protect your code correctly, your application will crash anyway. Crashlylytics, take this crashes and show them to you in a "readable" mode in the web application they have designed. If there is no crash, crashlytics will take anything. You can grab an exception in your code :
#try{
....
}
#catch(NSException ex){...}
in the critical parts, but you should always do that if you are afraid your application is going to crash or you find a potential error which can allow your application have a bad behavior and acting up. You can always force in your exception to send or track this error.
Hope it helps
The trick is to use the following :
http://support.crashlytics.com/knowledgebase/articles/202805-logging-caught-exceptions
Just use this :
Crashlytics.logException(new Exception("my custom log for crashLytics !!!"));
I use this and I get my non-fatal crash in about 5 minutes !

How can I catch EPIPE in my NSFIleHandle handling?

I'm having a problem with EPIPE in my iOS app, and it's not being caught in the #try/#catch/#finally block. How can I catch this signal (SIGPIPE, likely)...
I've built a "web proxy" into my app that will handle certain kinds of URLs - in this error case, it seems that the remote end (also in my app, but hiding in the iOS libraries) closes its end of the socket. I don't get a notification (should I? Is there something I should register for with the NSFileHandle that might help here?).
I've based this proxy on HTTPServer that Matt Gallagher put together (available here), and the problem is in a subclass of the HTTPRequestHandler class he put together. Here's the code (this code is the equivalent of the startResponse method in the base class):
-(void)proxyTS:(SSProxyTSResource *)proxyTS didReceiveResource:(NSData *)resource
{
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
CFHTTPMessageRef response =
CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Content-Type",
(__bridge CFStringRef)s_MIMEtype);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Connection",
(CFStringRef)#"close");
CFHTTPMessageSetBody(response,
(__bridge CFDataRef)resource);
CFDataRef headerData = CFHTTPMessageCopySerializedMessage(response);
#try
{
NSLog(#" -> writing %u bytes to filehandle...",[((__bridge NSData *)headerData) length]);
[self.fileHandle writeData:(__bridge NSData *)headerData];
}
#catch (NSException *exception)
{
// Ignore the exception, it normally just means the client
// closed the connection from the other end.
}
#finally
{
NSLog(#" *ding*");
CFRelease(headerData);
CFRelease(response);
[self.server closeHandler:self];
}
}
And here's what shows up in the console log when it crashes:
Jan 15 14:55:10 AWT-NoTouch-iPhone-1 Streamer[1788] <Warning>: [SSProxyTSResponseHandler proxyTS:didReceiveResource:]
Jan 15 14:55:10 iPhone-1 Streamer[1788] <Warning>: -> writing 261760 bytes to filehandle...
Jan 15 14:55:11 iPhone-1 com.apple.launchd[1] (UIKitApplication:com.XXX.Streamer[0xf58][1788]) <Warning>: (UIKitApplication:com.XXX.Streamer[0xf58]) Exited abnormally: Broken pipe: 13
It seems that because the other end closed the pipe the write() fails, so if someone can point me at how I can either discover that it's already closed and not try to write data to it OR whatever will make it not crash my program that would be very helpful.
The immediate problem of crashing with SIGPIPE is solved. I'm not entirely giggly about this solution, but at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving quite a bit better.
I've resolved this issue by examining further what's going on. In doing some research, I found that perhaps I should be using NSFileHandle's writeabilityHandler property to install a block to do the writing. I'm not fully sold on that approach (it felt convoluted to me), but it might help.
Writability-handler solution:
In doing some web searching on writeabilityHandler, I stumbled on Bert Leung's blog entry on some issues he was having in a similar area. I took his code and modified it as follows, replacing the #try/#catch/#finally block above with this code:
self.pendingData = [NSMutableData dataWithData:(__bridge NSData *)(headerData)];
CFRelease(headerData);
CFRelease(response);
self.fileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle)
{
int amountSent = send([thisFileHandle fileDescriptor],
[self.pendingData bytes],
[self.pendingData length],
MSG_DONTWAIT);
if (amountSent < 0) {
// errno is provided by system
NSLog(#"[%# %#] Error while sending response: %d", NSStringFromClass([self class]), NSStringFromSelector(_cmd), errno);
// Setting the length to 0 will cause this handler to complete processing.
self.pendingData.length = 0;
} else {
[self.pendingData replaceBytesInRange:NSMakeRange(0, amountSent)
withBytes:NULL
length:0];
}
if ([self.pendingData length] == 0) {
thisFileHandle.writeabilityHandler = nil;
// Hack to avoid ARC cycle with self. I don't like this, but...
[[NSNotificationCenter defaultCenter] postNotification:self.myNotification];
}
};
That worked fine but it didn't solve the problem. I was still getting SIGPIPE/EPIPE.
SIGPIPE be gone!
This wasn't a surprise, exactly, as this does pretty much the same thing as the former writeData: did but does it using send() instead. The key difference though is that using send() allows errno to be set. This was quite helpful, actually - I was getting a couple of error codes (in errno), such as 54 (Connection reset by peer) and 32 (Broken pipe). The 54's were fine, but the 32's resulted in the SIGPIPE/EPIPE. Then it dawned on me - perhaps I should just ignore SIGPIPE.
Given that thought, I added a couple of hooks into my UIApplicationDelegate in application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self installSignalHandlers];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
...
and applicationWillTerminate::
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self removeSignalHandlers];
[self saveContext];
}
-(void)installSignalHandlers
{
signal(SIGPIPE,SIG_IGN);
}
-(void)removeSignalHandlers
{
signal(SIGPIPE, SIG_DFL);
}
Now at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving.
I also switched back to the #try/#catch/#finally structure because it's more direct. Further, after ignoring SIGPIPE, the #catch block does get triggered. Right now, I'm logging the exception, but only so I can see that it's working. In the released code, that log will be disabled.

Resources