I have a code for fetching json. It works perfectly fine, when my device is connected to internet, but it crashes if there is no internet connection.
I have surrounded that particular line of code with try / catch block, but it doesn't seem to do the trick.
Of course, I can do a workaround, and first check if there is internet connection and then call the method I need, but I want to understand this.
Why #catch isn't triggered in this case, and what to do to handle this exception in this case?
Here is the code:
#try {
NSError *error;
NSMutableDictionary* json = [NSJSONSerialization
JSONObjectWithData:_jsonData
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
...
#catch (NSException *exception) {
[_indicator stopAnimating];
_indicator.hidden = YES;
[self popUp];
}
So, the exception occurs when trying to populate json dictionary.
Your JSON data _jsonData seems to be obtained before try~catch block, and then the source of the issue should be out of the block. Then you need to find out where the data is obtained from the internet, and install the try~catch block at there.
Related
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.
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.
What kind of errors can -[NSManagedObjectContext executeFetchRequest:error:] and -[NSFetchedResultsController performFetch:] return to the user and how should they be handled? I cannot find anything in the documentation about the possible errors for these methods. Also none of the error codes defined in CoreData/CoreDataErrors.h seem to apply to fetching.
Right now my error handling for Core Data fetches is just a NSAssert like this:
NSError *fetchError = nil;
NSArray *fetchedResults = [context executeFetchRequest: request error: &fetchError];
NSAssert( fetchedResults, #"Error fetching: %#", fetchError );
While testing I never had this assertion fail, but that doesn’t mean that this cannot fail. What are the best practices to handle those errors gracefully?
When executing a fetch request, I populate the expected array with an empty array instead of leaving it nil.
NSError *error;
NSArray *array = [context executeFetchRequest:request error:&error];
if (array == nil)
{
NSLog(#"Error retrieving array of values %#", error);
array = [NSArray array];
}
If you ever want to test your error handling, this answer details how to implement an NSPeristentStore that will give you an error every time.
Core Data Unit Testing - Unsure how to trigger error case in executeFetchRequest:error:
You already found CoreDataErrors.h, also see the Core Data Constants Reference
Possible errors that could occur would be SQLite query errors for example. I vaguely remember seeing something like this, when I used an operation in a predicate that was translated into something not supported by the SQLite version backing core-data.
If caught during development you use the NSError purely to debug. If this happens at run time in an already released application an option would be to fail gracefully and if possible be ask the user to specify a different search format.
First thing is to capture the error, but it entirely depends on the context of your code as to when you want to handle it gracefully or when you want to assert and stop anything else happening if things are going wrong.
Also remember that fetchedResults can return not nil, with no results (count == 0), which is not an error but you obviously might want to code against that.
NSError *fetchError = nil;
NSArray *fetchedResults = [context executeFetchRequest: request error: &fetchError];
if (fetchError) {
NSLog(#"Error with fetch: %#",error);
// Assert or do whatever is required ..
}
// Continue as normal ..
I always pass these errors up to the NSResponder chain, thus:
NSManagedDocument _document;
NSManagedObjectContext _moc;
NSError *error = nil;
NSArray *result = [_managedObjectContext executeFetchRequest:fr error:&error];
if (fetchedResults == nil && error) {
[_document presentError:error];
// or, if this isn't a document-based app, you can do
// [NSApp presentError:error];
// or, if this method is in an IBAction you can just do
// [sender presentError:error];
// and it'll just do the right thing
}
The default implementation in NSManagedDocument does an okay job of presenting these errors, except for situations when you are saving a doc and get multiple validation errors, in which case you need to write something special.
When in doubt, present NSError as soon as possible, and if you find yourself hard-coding a lot of retval checking for NSError, you problem might be more in what you're sending the error-returning function in the first place.
NSErrors, in general, are for users to resolve; NSExceptions are what the framework uses to let developers know what they need to deal with.
I'm trying to test the behavior of my app when connection fails. I am testing on an iPad with wifi turned off. When Restkit attempts a web service call, I get the following error:
CPL[7713:6203] E restkit.network:RKRequest.m:545 Failed to send request to https://xxxxxxxx/APNS_WebService/rest/operations/initializeDevice?deviceID=c4a17f855d3cc824b174b71908480d4e505ebfb221cb4643da9270a07344c367 due to unreachable network.
The problem is that I would like to handle this situation in a delegate callback method, but none of the delegate methods are being called. I have set the delegate on the request, and have requestDidFailLoadWithError, requestDidCancelLoad, requestDidTimeout, and objectLoaderDidFailWithError implemented. None of these are called.
Why aren't my delegates being called?
EDIT: After setting a breakpoint inside RKRequest.m, I see that the following line is in fact being executed:
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
However, my delegate methods are not getting called.
Here's where I set the delegate:
request = [client requestWithResourcePath:[NSString stringWithFormat:#"/initializeDevice?deviceID=%#",deviceID]];
request.delegate=self;
[request sendAsynchronously];
EDIT 2: Actually, the line in RKRequest.m that I posted above is just calling another method in RKRequest, except that it's not. Putting a breakpoint in didFailLoadWithError shows that this code is never reached. I don't get why that's not working.
Changing the performSelector to a regular method call appears on the surface to give me the behavior I'm looking for. Is this going to break anything? I guess I'm not sure why performSelector is being used to call a method in the same class.
EDIT 3: As requested, here's my delegate method:
-(void)request:(RKRequest *)request didFailLoadWithError:(NSError *)error{
NSLog(error.domain);
NSLog([NSString stringWithFormat:#"%d",error.code]);
NSLog(error.localizedDescription);
NSLog(error.localizedFailureReason);
[request reset];
[request send];
}
EDIT:
Actually, the line in RKRequest.m that I posted above is just calling another method in RKRequest, except that it's not. Putting a breakpoint in didFailLoadWithError shows that this code is never reached. I don't get why that's not working.
This is really strange. I would try doing a full clean of the project and rebuild.
As to what entails a direct call instead of using performSelector, you can see that afterDelay:
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
this will make the didFailLoadWithError: method be called at the next iteration of the run loop. I would keep this way of calling it.
You could try, though, with this alternative:
dispatch_async(dispatch_get_current_queue(), ^() {
[self didFailLoadWithError:error]; } );
I would suggest setting a breakpoint inside of the RestKit method you are using (I suppose sendAsynchronously) and check what happens. If you look into the method definition, the call to the delegate is effectively there:
} else {
self.loading = YES;
RKLogError(#"Failed to send request to %# due to unreachable network. Reachability observer = %#", [[self URL] absoluteString], self.reachabilityObserver);
NSString* errorMessage = [NSString stringWithFormat:#"The client is unable to contact the resource at %#", [[self URL] absoluteString]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
errorMessage, NSLocalizedDescriptionKey,
nil];
NSError* error = [NSError errorWithDomain:RKErrorDomain code:RKRequestBaseURLOfflineError userInfo:userInfo];
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
}
I am having this horrible crash out of my application that I have been trying to resolve for several days now. The code is an NSURLRequest to send a request to a web-service. I already use the web-service in production and I simply added this new service request. I finally added #try ... #catch around the offending code and it still crashes the application and does not "catch" anything.
How do I debug this?
I have used code inspection, including a peer review.
The code is like this ... where "httpClient" is an NSObject with:
id _delegateConnection;
id _delegateReceiver;
NSURLConnection *connection;
NSMutableURLRequest *urlServiceRequest;
NSMutableString *bodyRequest;
NSHTTPURLResponse *httpResponse;
NSMutableData *responseData;
NSError *error;
#try {
[httpClient setDelegateReceiver: self];
[httpClient buildRequest: dictParameters];
[httpClient executeRequest];
}
#catch (NSException *exception) {
NSLog(#"%s: %#", __FUNCTION__, exception);
}
#finally {
// Add code here ...
}
I found the problem and it was through good-fortune and code inspection in a different part of the code ("where the light was better" :)
As it turns out, I have a dictionary of parameters that get written to a JSON string, added to the HTTP body of the urlServiceRequest object listed in my sample code. What I finally stumbled upon was that the parameter container (NSObject, not NSDictionary) was not "retained", therefore it was probably "autoreleased", while still referenced within my buildRequest method, by the posting of the urlServiceRequest.
Poor memory management on my part led me astray in my troubleshooting.