How to maintain single try catch block in ios App - ios

I have 10 Classes in my project,At present i was placing try catch block in each and every method in all 10 classes to catch the exception which seems to be a bulky code.
Now i want to maintain a Single try catch block through out the project, means if any exception occurred in all my 10 classes that should be catched by try catch block.
Is it possible to do like this…
NSArray *arr=[[d1 objectForKey:#"Time"] componentsSeparatedByString:#" "];
NSArray *arr1=[[arr objectAtIndex:1] componentsSeparatedByString:#":"];
Here my object “Time” is coming from the Service.
so if the object is empty it may cause to
Array out of index Exception.
I know which i can solve by comparing the length of String.So, if suppose if forgot to compare length in some cases.
In that case i want to catch the exception and i want to post it to my server.
#catch (NSException *exception) {
NSString *stacktrace=[exception.reason stringByReplacingOccurrencesOfString:#"<" withString:#""];
stacktrace=[stacktrace stringByReplacingOccurrencesOfString:#">" withString:#""];
// NSString *strClass = NSStringFromClass([self class]);
NSString *strClass = [NSString stringWithFormat:#"%# : %#",NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
Item* myStore = [Item sharedStore];
myStore.strErrorDescription=exception.name;
myStore.strErrorStack=stacktrace;
myStore.strErrorPageName=strClass;
[myStore PostError];// To Post the exception to My server.
ErrorView *errorView=[[ErrorView alloc]initWithNibName:#"ErrorView" bundle:nil]; [self presentViewController: errorView animated:NO completion:nil];
}
Thank you.

The real answer here is You're Doing It Wrong.
Exceptions in Objective-C are different from those in other languages like Java or Ruby. In particular, they tend to be "non-recoverable", meaning that once some code throws an exception, a number of assumptions about the state of memory allocations, etc become false, and your program is probably on its way to crashing.
The overall approach Apple recommends is to treat exceptions as a symptom of a programmer error. The example you give is the same one they do: neglecting to check the length of an NSArray. You should aim to find and eliminate those during your development process; the best way to do that is probably to let your program crash as a result of the exception, then find and fix the offending code.

Related

Objective-C : appending a NSString to NSException reason

I am working on some API (Crittercism) to report handled exceptions in the client to server.
The client API takesNSException as the input parameter. I have to add some application context info string(NSString) also to theNSException before calling the API.
How I can do it using Objective-C.
NSString* appContextString;
NSString *test = #"test";
unichar a;
int index = 5;
#try {
a = [test characterAtIndex:index];
}
#catch (NSException *exception) {
// add app context to exception before reporting to Crittercism.
[Crittercism logHandledException:exception];
}
I have to append appContextString to exception.
You could build a new NSException object from attributes of the old one, but that seems very messy. As you are just logging the handled exception, I would simply log the "app context" before that:
#try {
a = [test characterAtIndex:index];
}
#catch (NSException *exception) {
NSString *appContext = #"...";
[Crittercism leaveBreadcrumb:appContext];
[Crittercism logHandledException:exception];
}
You have to copy it. This can be done easily. An instance of NSExeption has three important properties:
name
reason
user info
When copying it, you can add a string to the user info dictionary:
NSMutableDictionary *userInfo = [exception.userInfo mutableCopy];
userInfo[#"YourPrivateKey"] = contextString;
NSException *extendedException = [NSException exceptionWithName:exception.name reason:exception.reason userInfo:userInfo];
I think as a caveat that you will lose the call stack. You can read that and put it in the user info dictionary, too. I do it a similar way on Objective-Cloud.
userInfo[#"CallStackSymbols2] = exception.callStackSymbols;
Analogous with call stack return addresses.
I do not think that this is very nice, because the call stack becomes a part of the exceptions's user info instead of the exception itself. For me this is no caveat, because I sent the exception as JSON, therefore the call stack as JSON object. I cannot say, whether this is a problem for you.

PFAnalytics trackEvent exc_bad_access

I'm trying to use Parse analytics in my app to track errors, but when the trackEvent method is called, i receive an EXC_BAD_ACCESS.
I use the code from Parse documentation
NSString *codeString = [NSString stringWithFormat:#"%d", [error code]];
[PFAnalytics trackEvent:#"error" dimensions:#{ #"code": codeString }];
I though i was doing something wrong with the dimensions NSDictionnary, so i tried:
[PFAnalytics trackEvent:#"error"];
But I'm still receiving the same signal. I know that EXC_BAD_ACCESS is thrown as a result of an illegal memory access, but in that case i'm just calling static method with a simple string as a parameter, so I don't know what i'm doing wrong.
Have you encountered the EXC_BAD_ACCESS in that case, and how did you deal with it ?

Data loads but won't save iPhone app

I've been stuck on this for approximately two weeks. I hate posting things that have been asked a lot but I really have gone through them all.
I used Ray Wenderlich's tutorial for saving data in an iPhone app.
http://www.raywenderlich.com/tutorials
So that is the setup I have going on in my app. I'm saving very simple objects. My Card object consists of a name, type, and image. That's all. So the tutorial is quite close to mine. Which is making this more frustrating.
The thing is, I have some NSLog statements in there for loading. I have it displaying the folder it's using to load and what objects it does load. Right now it is displaying this.
Loading cards from /Users/zach/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/E3DB01FD-A37E-4A69-840B-43830F2BDE2C/Library/Private Documents
2013-11-04 00:02:50.073 CardMinder[84170:a0b] ()
So it seems to be trying to load them, but there's nothing there to load. Here is my function to save data.
- (void)saveData {
if (_data == nil) return;
[self createDataPath];
NSString *dataPath = [_docPath stringByAppendingPathComponent:kDataFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:_data forKey:kDataKey];
[archiver finishEncoding];
NSLog(#"%#",dataPath);
NSLog(#"%#",data);
[data writeToFile:dataPath atomically:YES];
}
Which is really just what's posted in that tutorial. I know if you feel generous enough to help me out i'll have to post some more code but I don't want to flood the post with useless stuff so just let me know and i'll get it out here.
I really appreciate anyone that can help, I have recently entered the desperation state and need help.
Thanks
UPDATE
NSError *error;
[data writeToFile:dataPath options:NSDataWritingAtomic error:&error];
NSLog(#"error: %#", error.localizedFailureReason);
These are the methods for the CardData class. I'm doing the name, type, and a bool here.
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:kNameKey];
[aCoder encodeObject:_cardType forKey:kTypeKey];
[aCoder encodeBool:_checkedOut forKey:kOutKey];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSString *name = [aDecoder decodeObjectForKey:kNameKey];
NSString *cardType = [aDecoder decodeObjectForKey:kTypeKey];
BOOL checkedOut = [aDecoder decodeBoolForKey:kOutKey];
return [self initWithName:name cardType:cardType _Bool:checkedOut];
}
UPDATE 2
I just put some more NSLog statements in and I found out that when I press the "Save card" button in my app, it doesn't seem to execute the saveData function at all. I have log statements galore in that saveData function and when I click the saveCard button it doesn't show any of those logs. Why would that be happening?
This is my saveButton code.
- (IBAction)saveNewCard:(id)sender
{
NSString *cardName = self.nameField.text;
_cardDoc.data.name = cardName;
CardDoc *newCard = [[CardDoc alloc] initWithName:cardName cardType:cardTypeString _Bool:NO image:chosenIcon];
[_cardDoc saveData];
NSLog(#"Card save button pressed!");
CardViewController *cardViewController = (CardViewController *)[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
[cardViewController.cards addObject:newCard];
[self.navigationController popToRootViewControllerAnimated:YES];
}
You should use writeToFile:options:error: instead of writeToFile:atomically:; that will give you an error message that should prove helpful. (The equivalent to atomically:YES is the option constant NSDataWritingAtomic.) Make sure you're getting back a return value of YES; if not, the error should be set.
If you're getting a value of NO but the error is not set, it means you're messaging nil. A quirk of Objective-C is that messaging nil is completely valid. If the method is defined to return something, you'll even get a result: 0 or equivalent (NO, nil, etc.)
In this case, you're messaging _cardDoc. There's no return result to detect. This is a bit harder to defensively code around, but [_cardDoc saveData] is actually [nil saveData]. The debugger will just breeze past the line.
Generally, if something absolutely should not be nil, you can use NSAssert:
NSAssert(_cardData, #"_cardData should not be nil");
[_cardData saveData];
But use this sparingly; you'll probably come to usually appreciate this behaviour.
A few things.
Post the results of your log statements so we know what you are seeing.
In order for your approach to work, your _data object needs to conform to the NSCoding protocol. That means you need to add the protocol declaration to your interface, and implement the methods encodeWithCoder and initWithCoder.
In those methods you need to save all the state data for your object / load the state back into your object.
Those methods are the most likely source of problems with your code. Post those methods if you need help with them, and walk though them in the debugger.
You might also look at the NSKeyedArchvier class method archivedDataWithRootObject. That method takes an object and encodes it into an NSData object in one step. The method archiveRootObject:toFile: take it a step further, and writes the data directly to a file for you.
NSKeyedUnarchiver has the corresponding methods unarchiveObjectWithData and unarchiveObjectWithFile to recreate your object from data/a file.

Need a generic way to extract error text

I've developed some iOS 6.1 code to deal with NSError. But, I'm not happy with it. It is at best a hack:
-(bool) reptErrAtModule: (NSString *) module
atSubr: (NSString *) subr
atFunc: (NSString *) func
withErr: (NSError *) err
{
id value = [[err userInfo] objectForKey: NSUnderlyingErrorKey];
NSString * errDesc = (value != nil) ?
[value localizedDescription]:
(NSString *)[[err userInfo] objectForKey: #"reason"];
NSLog( #"ERR -> %#",[NSString stringWithFormat:
#"(%#>%#) %# failed! %#",module,subr,func,errDesc] );
}
I had a simpler form (without the (NSString *)[[err userInfo] objectForKey: #"reason"] case) and it worked for errors that I got back from calls to removeItemAtPath.
But then I got an error back from this code:
NSPersistentStore * entStor =
[myPerStoCor addPersistentStoreWithType: NSSQLiteStoreType
configuration: nil
URL: [NSURL fileURLWithPath: Path]
options: nil
error: &err];
And my routine failed to extract the error. So I added the #"reason" logic because I could see the text I wanted in the Info data in the debugger.
Now the code works with both types of errors but I'm thinking this is not the way to do this. There must be a better, more generic way to deal with all the types of errors stuff the system can give you back in NSError.
I use this:
NSString *description = error.localizedDescription;
NSString *reason = error.localizedFailureReason;
NSString *errorMessage = [NSString stringWithFormat:#"%# %#", description, reason];
For debugging purposes, you ideally want to log out the entire contents of the error. Roughly speaking this is the domain, code, and userInfo. Bear in mind that userInfo might well include an underlying error, which you want to apply the same logic to. And in some cases, the error might supply a description (or failure reason etc.) which isn't present in the userInfo.
If you scroll down my blog post at http://www.mikeabdullah.net/easier-core-data-error-debugging.html, there's a snippet there showing how to generate a dictionary representation of an NSError object, and then get a string representation of that. This is pretty handy for debugging/logging purposes.
For presentation to users though, -[NSError localizedDescription] is expressly designed for such purposes. -localizedFailureReason serves a similar role, tending to specify what went wrong, without the context of the operation being tried. (One way to think of it is localizedDescription = task desceription + localizedFailureReason)

ios - is there a reliable crash-reporting system for the iPhone/iPad ecosystem?

I know in Android, there is a pretty reliable built-in system for notifying me of crashes that happen. They almost immediately write the stack trace and some other info into a Google doc. it works tremendously well.
I started researching the same thing for ios, and I am not finding anything similar that is equally effective. I read this article: Xcode storyboard: Why does the iPad storyboard show iPhone sized views?
but I am not sure if this is still the case. Could anyone recommend me what to use for crash reports that happen on user devices?
Thanks!
I'm using Flurry with an uncaughtExceptionHandler and GTMStackTrace
The exception handler can look like this:
void uncaughtExceptionHandler(NSException *exception)
{
#try
{
NSString *fullBacktrace = GTMSBStackTraceFromException(exception);
NSMutableString *backtrace = [NSMutableString stringWithUTF8String:""];
NSArray *backtraceArray = [fullBacktrace componentsSeparatedByString:#"\n"];
for (id entry in backtraceArray)
{
NSRange testRange = [entry rangeOfString:[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"]];
if (testRange.length)
{
[backtrace appendString:entry];
}
}
NSCharacterSet *whitespaces = [NSCharacterSet whitespaceCharacterSet];
NSPredicate *noEmptyStrings = [NSPredicate predicateWithFormat:#"SELF != ''"];
NSArray *parts = [backtrace componentsSeparatedByCharactersInSet:whitespaces];
NSArray *filteredArray = [parts filteredArrayUsingPredicate:noEmptyStrings];
NSString *strippedBacktrace = [filteredArray componentsJoinedByString:#" "];
[FlurryAnalytics logError:#"uncaughtExceptionHandler"
message:[NSString stringWithFormat:#"%#", strippedBacktrace ? strippedBacktrace : #"no matching backtrace"]
exception:exception];
}
#catch (NSException *exception)
{
NSLog(#"whoa! could not handle uncaught exception!");
[FlurryAnalytics logError:#"uncaughtExceptionHandler"
message:#"no matching backtrace"
exception:exception];
}
}
Testflight has a good crash log too.
What you can do is create a new uncaught exception handler, then register it via NSSetUncaughtExceptionHandler. That way, each crash can be intercepted just before the kill, and you can log it / save it somewhere to upload.
(I've personally used the method described in this link : http://cocoawithlove.com/2010/05/handling-unhandled-exceptions-and.html)
Setting an uncaughtexception handler will only give you a subset of crash reports, and also the details of the reports you get that way are very limited. You don't get stack traces of all threads, you don't get the stacktrace of the exception where the crash really happened, and you don't get the line numbers of your code where the crash happened. In addition you don't get any crash reports caused by signal handlers.
In addition you should NOT run any objective-C code once a crash occurs since it is not async-safe. See this article for more information about it: Reliable Crash Reporting
If posted some more details on what you can do as answers to the following questions: Crash analytics and reporting for iOS and iOS crash log catch, debug info.. Catch and send via email to the Dev team

Resources