Objective-c passing error parameter to inside method - ios

Its a common pattern to add an error output parameter when writing Objective-c methods.
As far as I know this is how you create a method that return an error if something is wrong:
- (void)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
Now there are times when you just want that error parameter to reflect an error occurred in some other method you use inside your method, lets say:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:nil];
}
So I thought ok, I'll just pass the error parameter to the inside method, like this:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (error) {
NSLog(#"error %#", error);
}
}
But this approach has two issues:
1. the if (error) check returns YES even if there is no error.
2. the log line generates this warning: Format specifies type 'id' but the argument has type 'NSError *__autoreleasing *'
So what am I doing wrong here?

There are a couple of things wrong. Firstly the NSError object should not be used to test for errors, instead use the method's return value. Therefore your first example method should return BOOL to indicate success:
- (BOOL)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
if (error) { // Check it's been passed, and if so create the error object.
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
return success;
}
And test for results being nil, not error being non-nil:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (!results) {
if (error && *error)
NSLog(#"error %#", [(*error) localizedDescription]); // Here is your 2. I think.
else
NSLog(#"Unknown error");
}
}
Secondly the error parameter is commonly optional (as seen in your code where you pass nil, which should be NULL actually). Therefore you need to test if it's been passed before dereferencing it (see code above).
However to answer your overall question, yes it's good to pass the error parameter along to subordinate method calls and is commonly used.
I have no idea about your 2. until you update your code... standing by. I think your 2. issue is because you need to use [error localizedDescription] with NSLog().

You are passing address of error not actual error this means &error
So you need to derefrence the error pointer.NSError *__autoreleasing * you are taking parameter as address of error.We generally do this because objective c can return only one value.But error need to be known from where we are calling the mehod so passing it as address of error will make change to error if there an error comes in calle function.
So if any error comes in below line
NSArray *results = [context executeFetchRequest:request error:error];
than it is automatically know to calle function i.e doSomethingWithObj
if (*error) {
NSLog(#"error %#", (*error).description);
}
Use
NSLog(#"error %#", (*error).description);
instead of
NSLog(#"error %#", (error).description);
you have to pass &error

Related

How can I add Status code to an Error?

I have a 'JSON' data with different status codes, as shown in the image, I'm using 'AFHTTPSessionManager', And if we call the API I get 200 as success code, Apart from that in my response object I have status codes So I want to do different operations based on the status codes, For this I have written code like this..
-(void)validateOTP:(NSString *)OTP andUserID:(NSString *)userID withCompletion:(void(^)(NSDictionary* dictionary, NSError* error))completion
{
UTValidateOTPRequest *request = [UTValidateOTPRequest validateOTPRequestWithOTP:OTP andUserID:userID];
NSDictionary *paramDict = [request dictionaryRepresentation];
NSURLSessionDataTask *task = [self postRequestToResouce:[NSString stringWithFormat:#"%#/ValidateOTP",kServerCommonEndPoint] arguments:paramDict successHandler:^(NSDictionary *response) {
NSNumber *statusCode = response[kStatusCodeKey];
if (statusCode.integerValue == UTStatusCodeSuccess) {
completion(response, nil);
}
else {
NSError *error = [NSError mapStatusCodeToError:statusCode.integerValue details:response];
completion(nil, error);
}
} errorHandler:^(NSError *error) {
NSError *newError = [NSError mapStatusCodeToError:error.code details:error.userInfo];
completion(nil, newError);
}];
[self addDataTask:task];
}
As you can see, even inside success handler I'm passing error and calling a category method we have created.
NSError *error = [NSError mapStatusCodeToError:statusCode.integerValue details:response];
This method implemented as follows
+ (NSError *)mapStatusCodeToError: (NSInteger)statusCode details:(NSDictionary*) errorInfo
{
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSString *domain = [bundleIdentifier stringByAppendingString:kErrorDomainKey];
NSString *errorMessage = nil;
if (errorInfo[kErrorMessageKey] && ![errorInfo[kErrorMessageKey] isEqualToString:kEmptyString]) {
errorMessage = errorInfo[kErrorMessageKey];
}
else{
// use common message
errorMessage = kInternetNotAvailableMessage;
}
NSDictionary *userInfo = #{NSLocalizedDescriptionKey:NSLocalizedString(errorMessage, nil)};
NSLog(#"User INFO : %#",userInfo);
NSError *internetUnavailableError = [NSError errorWithDomain:domain
code:NSURLErrorNotConnectedToInternet
userInfo:userInfo];
NSLog(#"Error Code : %ld",(long)internetUnavailableError.code);
return internetUnavailableError;
}
Here I want to use the statusCode that I'm passing as parameter to this method so that I can get that status code where I'm calling this method
[[UTServerManager sharedInstance] validateOTP:self.enterOTPText.text andUserID:self.userId withCompletion:^(NSDictionary *dictionary, NSError *error) {
// Here I want to get the status code of error not like -1009 , but what ever statusCode that I'm getting from the API response.
}];
So in this method can I get the response status code if it is not success code means as you see in the image in the first response is success and remaining are error responses.
And I know that I can use the statusCode in the category method but I dont know how to use it, If I store this status-Code in the category method for error as above , that I'm passing as a parameter to the methods then I can call them where ever I require How to get this ?
And my restriction is I should these methods only but I have to add the response status code to error ??
Looks like you are passing a userInfo dict to the NSError. You could add to this dictionary prior to creating the NSError you are returning, and then later it would be available in the NSError callback.

openParentApplication:reply: error with asynchronous network call in containing app

I'm getting stuck with an error when using my Watchkit Application. When I launch it, I ask the containing iOS app to get some data from network. The problem is that I get an error saying the containing app never calls 'reply()' :o But looking at my code, it should call it.
I tried to debug every step from openParentApplication to the 'reply()' call, and it seems to work well =X
Here is my code in the Watchkit extension
- (void)initDiaporamasWithSuccess:(void (^)())success andFailure:(void (^)(NSError*))failure {
NSLog(#"Ask to load diapos");
__weak typeof(self) weakSelf = self;
[WKInterfaceController openParentApplication:#{#"watchKit": #"watchKit.initDiapos"} reply:^(NSDictionary *replyInfo, NSError *error) {
if (error) {
NSLog(#"%#", error);
if (failure) {
failure(error);
}
return;
}
NSLog(#"got items : %#", replyInfo[#"diapos"]);
weakSelf.diaporamas = replyInfo[#"diapos"];
[weakSelf setDiaporama:replyInfo[#"firstDiapo"] AtIndex:0];
if (success) {
success();
}
}];
}
The result should be an NSDictionary containing an NSArray with some diaporamas basic informations, and an object (Diapo) containing the full informations of the first diaporama (e.g. self.diaporamas[0])
And here is the code in the containing app's AppDelegate :
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
// Maybe we could handle multiple watchKit extension calls that way ?
// Something like a key-value 'protocol' to run the right block of code
NSString *watchKitCall = userInfo[#"watchKit"];
NSLog(#"watchKit handled");
if ([watchKitCall isEqualToString:#"watchKit.initDiapos"]) {
[AppDelegate watchInitialObjects:^(NSDictionary *info) {
NSLog(#"Managed to get initial infos");
reply(info);
} failure:^(NSError *error) {
NSLog(#"Fail : %#", error);
reply(#{#"error": error});
}];
}
}
+ (void) watchInitialObjects:(void (^)(NSDictionary *info))success failure:(void (^)(NSError *error))failure {
NSDictionary *parameters = #{#"site" : #(14), #"limit" : #(10)};
[AppDelegate requestDiapoListWithParams:parameters success:^(NSArray *items) {
if ([items count] == 0)
{
NSError *error = [NSError errorWithDomain:#"com.domain.app" code:404 userInfo:nil];
failure(error);
return;
}
Diapo *firstDiapo = [items firstObject];
[AppDelegate requestDiapoDetailWithDiapo:firstDiapo success:^(Diapo *diapo) {
if (!diapo)
{
NSError *error = [NSError errorWithDomain:#"com.domain.app" code:404 userInfo:nil];
failure(error);
return;
}
NSDictionary *result = #{
#"firstDiapo" : diapo,
#"diapos" : items
};
success(result);
} failure:^(NSError *error) {
failure(error);
}];
} failure:^(NSError *error) {
failure(error);
}];
}
In the watchKitHandler, I call watchInitialObjects to get the diaporamas array and the first diaporama's informations.
In the watchInitialObjects, I make a first network call to get the array, and on success, I make an other network call to get the firs diaporama informations.
To make the calls and map the JSON into objects, I use RESTKit
I really don't get what could be the error =x
UPDATE
I forgot to write the error I get, here it is :
Error Domain=com.apple.watchkit.errors Code=2 "The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]" UserInfo=0x7fcb53e12830 {NSLocalizedDescription=The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}
And I kept trying to know why I get this error, and I think I found it:
It seems that there is a (very little) timeout to do the work in the containing app. But I mapped the JSON data I received directly in the containing app and then, send those custom objects in the reply(). But when I removed the mapping part, it worked well !
So...that's why I think that was the problem =X
Does anybody could approve my thoughts or corrects me ?
After hours of searching and testing different codes, I finally found my problem...and it's obvious when we read the Apple documentation about 'application:handleWatchKitExtensionRequest:reply:' seriously...
here is the answer : (it's in the documentation)
The contents of the dictionary must be serializable to a property list file.
Which means that objects can ONLY be dictionaries, arrays, strings, numbers (integer and float), dates, binary data, or Boolean values
...I feel dumb ><

How to cast to NSError from this [value isKindOfClass:[NSError class]]?

I am using external component to connect to SOAP service. When I receive result it is returned as object value, which can contain NSError or resulting object.
In case of an error I want to extract code and message of the error. How to cast value to NSError?
I receive [value isKindOfClass:[NSError class]] to check if an error is returned.
if ([value isKindOfClass:[NSError class]]) {
NSError *error = (NSError *)value;
NSLog(#"It didn't work: %#", [error localizedDescription]);
} else {
// returned object
}

iOS return bool value in block

I am trying to return a bool value from a class method I call that has a block in it. I get the error, Incompatible block pointer types sending.... How would I get around this? I just want to know if the class method I call completes with or without error...
+ (BOOL)saveSelectedDepartmentsToParse:(NSMutableDictionary *)dictionary {
NSArray *array = [dictionary allKeysForObject:#"YES"];
NSMutableArray *trimmedArray = [[NSMutableArray alloc] init];
for (NSString *string in array) {
NSString *final = [string removeAllInvalidCharacters];
[trimmedArray addObject:final];
}
NSLog(#"Array = %#", trimmedArray);
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation removeObjectForKey:#"channels"];
[currentInstallation addObjectsFromArray:trimmedArray forKey:#"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
}
else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
}
}];
}
I just want to know if the class method I call
This is a misunderstanding of how asynchronous code works. When you are supplying a block to saveInBackgroundWithBlock:, that code is not executed straight away. It's executed at some later point by the Parse framework, and whichever part of Parse that does so would get the return value if the block were defined to have one, which it isn't. Your block isn't executed at the point at which you write it, so you can't return anything at the point at which you write it.
Your code isn't calling the block, and you can't return values to your code from it. It doesn't make sense to do so. If another part of your code wants to know when the saving has finished, you'll need to use some other mechanism than return values, such as calling a method from your block, posting a notification, or Key-Value Observing.
From the block keyword InBackground:
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
} else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
}
}];
I guess the block is called asynchronously.
If you want to get the result, you can wait here until the block is executed, but this make the saveInBackgroundWithBlock useless.
So NSNotification may be better:
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error == nil) {
NSLog(#"Parse Save Succeeded");
[self saveDepartmentsDictionary:dictionary];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationParseSaveSucceeded object:nil];
} else {
NSLog(#"Parse Save Failed, %#", error.localizedDescription);
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationParseSaveFailed object:nil];
}
}];

Is an NSError reusable?

e.g.
NSError *error = nil;
if ([something error:&error]) {
// ...
}
// ...
if ([somethingElse error:&error]) {
// ...
}
Will I run into any problems if I use the same NSError without resetting it to nil?
Ordinarily I'd say it doesn't matter, but I worry that someone else would use one error when they meant to use the other.
No problems, but to do that check error before passing the same pointer as argument. If the something method has an error and the somethingElse too, the error variable will have a pointer to the second one. Think you pass a pointer to a pointer to an object.
NSError *error = nil;
if ([something error:&error]) {
// ...
}
if (error) {
//...
}
if ([somethingElse error:&error]) {
// ...
}
I don't think you will have problems with it. Your NSError variable will be rassigned only.

Resources