How can I refactor duplicated codes for error? - objective-c

I'm using block for my APIs and the API class throws error via block like below code.
[HJHLifeAPI deletePlantWithIdentifier:identifier completionHandler:^(NSError *error) {
if (error) {
[[error alertView] show];
return ;
}
[self refresh:self.refreshControl];
}];
But the problem is that I use this pattern of codes in several places. As a result, I should write several duplicated codes for error handling. Is there any way to refactor this code? I think exception can be one solution, but I think Apple don't encourage developers to use it.

It's up to How your HJHLifeAPI is designed.
I usually use AFNetworking for API things and here's an example.
// This is the method like deletePlantWithIdentifier:
// It actually invoke __requestWitPath:
- (void)requestSomethingWithId:(NSString *)memId done:(NetDoneBlock)done
{
NSMutableDictionary *param_ = #{#"key":#"id"};
[self __requestWithPath:#"/apiPath.jsp" parameter:param_ done:done];
}
#pragma PRIVATE
- (void)__requestWithPath:(NSString *)apiPath parameter:(NSDictionary *)parameter done:(NetDoneBlock)done
{
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:SERVER_URL]];
AFHTTPRequestOperation *operation = [manager POST:apiPath parameters:parameter constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
done();
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Error Handle Here
}];
[operation start];
}
You can handle all errors in one __request....

Create the block
void(^errorHandler)(NSError *error) = ^(NSError *error) {
if (error) {
[[error alertView] show];
return ;
}
[self refresh:self.refreshControl];
}
Save it somewhere (don't forget to copy it)
self.errorHandler = errorHandler;
Reuse it everywhere:
[HJHLifeAPI deletePlantWithIdentifier:identifier completionHandler:self.errorHandler];

Related

Objective-C AFnetworking: stopping a request

Evening, I'm working with the Marvel-API, trying to download all the characters.
To download all the characters you have to do multiple requests, in each request you can specified the limit and the offset.
So I've set the limit at the max of 100 and for every request I increase the offset by 100.
Doing that, I do infinite request. Of course.
So I thought that I should stop when the "results" array retrieved from the JSON object is empty.
So the logic should be good, I keep requesting characters 100 by 100 until there are no more to retrieve.
But of course working with networking and async code isn't always so easy. And obviously I got stocked.
I'm sure that the problems is in these lines of code:
#pragma mark - Requesting data
-(void)getData {
NetworkManager *networkManager = [NetworkManager alloc];
while(self.requestMustEnd == false) {
NSLog(#"offset: %d", networkManager.offset);
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:networkManager.getUrlPath parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[self parseResponseData:responseObject];
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[networkManager increaseOffset];
}
}
#pragma mark - Parsing Method
-(void)parseResponseData:(NSDictionary *)responseDictionary {
NSArray *marvelArray = [[responseDictionary objectForKey:#"data"] objectForKey:#"results"];
if (marvelArray.count == 0) {
self.requestMustEnd = true;
}
for(NSDictionary* marvel in marvelArray)
{
Character *currentMarvelEntity = [[Character alloc] initWithMarvel:marvel];
//NSLog(#"currentMarvelEntity %#", currentMarvelEntity.name);
[self.marvelCharacters addObject:currentMarvelEntity];
}
[self.tableView reloadData];
}
The key part to stop the request is:
if (marvelArray.count == 0) {
self.requestMustEnd = true;
}
But, still, it never end to request. it is not for the if condition, I'm sure. But probably because, having an async code, the getData func no matter what keep requesting data.
Any tips?
This post may help. Try:
[manager.operationQueue cancelAllOperations];

CKDiscoverAllContactsOperation not fetching contacts

I am using CKDiscoverAllContactsOperation but its not working fine for me.
-(void)queryForAllUsers: (void (^)(NSArray *records))completionHandler {
CKDiscoverAllContactsOperation *op = [[CKDiscoverAllContactsOperation alloc] init];
[op setUsesBackgroundSession:YES];
op.queuePriority = NSOperationQueuePriorityNormal;
[op setDiscoverAllContactsCompletionBlock:^(NSArray *userInfos, NSError *error) {
if (error) {
NSLog(#"An error occured in %#: %#", NSStringFromSelector(_cmd), error);
//abort();
} else {
NSLog(#"Number of records in userInfos is: %ld", (unsigned long)[userInfos count]);
dispatch_async(dispatch_get_main_queue(), ^(void){
completionHandler(userInfos);
});
}
}];
[self.container addOperation:op];
}
The container which I'm using is publicCloudDatabase.
The search only works if different users activate the app, approved to be Discoverable and have the other person's iCloud email address in their Contacts.
You should use the discoverAllContactUserInfosWithCompletionHandler on the container like this:
[self.container discoverAllContactUserInfosWithCompletionHandler:^(NSArray *userInfos, NSError *error) {
..
}
this function will only return the contacts that can be linked to an iCloud account and the person has also started up your app.

Dealing with AFNetworking2.0 asynchronous HTTP request

I am very new to the concept of asynchronous programming, but I get the general gist of it (things get run in the backround).
The issue I'm having is I have a method which utilizes AFNetworking 2.0 to make an HTTP post request, and it works for the most part.
However, I can't figure out how to get the method to actually return the value received from the response as the method returns and THEN gets the value from the response.
-(int) registerUser
{
self.responseValue = 000; //Notice I set this to 000 at start of method
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
NSDictionary *parameters = #{ #"Username": #"SomeUsername" };
[manager POST:#"http://XXX/register"
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
NSError *err = nil;
self.responseValue = [[responseObject objectForKey:#"res"] intValue];
//Note: This^ value returns 99 and NSLogs confirm this
}
failure:^(AFHTTPRequestOperation *operation, NSError *err)
{
NSLog(#"Error: %#", err);
}];
return self.responseValue; //This will return 000 and never 99!
}
Whats the 'proper' way to handle this situation? I've heard whispers of using a 'callback', but I don't really understand how to implement that in this situation.
Any guidance or help would be awesome, cheers!
The issue is that the POST runs asynchronously, as you point out, so you are hitting the return line well before the responseValue property is actually set, because that success block runs later. Put breakpoints/NSLog statements in there, and you'll see you're hitting the return line first.
You generally do not return values from an asynchronous methods, but rather you adopt the completion block pattern. For example:
- (void)registerUserWithCompletion:(void (^)(int responseValue, NSError *error))completion
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
NSDictionary *parameters = #{ #"Username": #"SomeUsername" };
[manager POST:#"http://XXX/register"
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
int responseValue = [[responseObject objectForKey:#"res"] intValue];
if (completion)
completion(responseValue, nil);
}
failure:^(AFHTTPRequestOperation *operation, NSError *err)
{
NSLog(#"Error: %#", err);
if (completion)
completion(-1, err); // I don't know what you want to return if it failed, but handle it appropriately
}];
}
And then, you could use it as follows:
[self registerUserWithCompletion:^(int responseValue, NSError *error) {
if (error)
NSLog(#"%s: registerUserWithCompletion error: %#", __FUNCTION__, error);
else
NSLog(#"%d", responseValue);
// do whatever you want with that responseValue here, inside the block
}];
// Needless to say, don't try to use the `responseValue` here, because
// `registerUserWithCompletion` runs asynchronously, and you will probably
// hit this line of code well before the above block is executed. Anything
// that is dependent upon the registration must called from within the above
// completion block, not here after the block.
Note, I'd suggest you retire that responseValue property you had before, because now that you're using completion blocks, you get it passed back to you via that mechanism, rather than relying on class properties.
Check this one and use search ;-))
Getting variable from the inside of block
its a lot of duplicates already!
:-)

unrecognised selector error for AFHTTPRequestOperation setAuthenticationAgainstProtectionSpaceBlock:

I'm trying to run the following code in IOS.
AFHTTPRequestOperation *requestOperation = [self.httpClient HTTPRequestOperationWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url.text]]
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"reply data = %#", [[NSString alloc] initWithData:operation.responseData encoding:NSUTF8StringEncoding]);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[requestOperation setAuthenticationAgainstProtectionSpaceBlock:^BOOL(NSURLConnection *connection, NSURLProtectionSpace *protectionSpace) {
return YES;
}];
I then get an error like this:
-[AFHTTPRequestOperation setAuthenticationAgainstProtectionSpaceBlock:]: unrecognized selector sent to instance 0x75a81f0
As far as I can see the block that I'm passing has the correct return type and parameters. What am I doing wrong here?
AFURLRequestOperation conditionally compiles with certain delegate callback methods available, depending on whether _AFNETWORKING_PIN_SSL_CERTIFICATES_ is defined or not.
If it is (which is the default when installing from CocoaPods), setWillSendRequestForAuthenticationChallengeBlock: will be available. Otherwise setAuthenticationAgainstProtectionSpaceBlock: and setAuthenticationChallengeBlock: will be available.
setWillSendRequestForAuthenticationChallengeBlock corresponds to connection:willSendRequest:forAuthenticationChallenge:, which is the preferred delegate method to handle challenges.

Switching from AFNetworking to RestKit

I started developing my application using AFNetworking. Everything went OK till I want to use core data. I know there is an additional class (AFIncrementalStore) for that. But because I'm new to IOS-development and there is not a lot of information about that. I decided to switch to RestKit because here is a lot more information. Now, I followed a tutorial about AFNetworking. Here I created an API class which this method in it.
+(API *)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^ {
sharedInstance = [[self alloc]initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
#pragma mark - init
//intialize the API class with the destination host name
-(API *)init
{
//call super init
self = [super init];
if (self != nil){
//initialize the object
user = nil;
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
-(void)loginCommand:(NSMutableDictionary *)params onCompletion:(JSONResponseBlock)completionBlock{
NSLog(#"%#%#",kAPIHost,kAPILogin);
NSMutableURLRequest *apiRequest = [self multipartFormRequestWithMethod:#"POST" path:kAPILogin parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData>formData){
//TODO: attach file if needed
}];
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
//success!
NSLog(#"SUCCESSSS!");
completionBlock(responseObject);
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
//Failure
NSLog(#"FAILUREE!");
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
This handles the communication between my webservice and application.
In the viewControler itself I call this method like this.
/* [[API sharedInstance] loginCommand:[NSMutableDictionary dictionaryWithObjectsAndKeys:_txtLogin.text,#"email",_txtPass.text,#"pwd", nil] onCompletion:^(NSDictionary *json){
//completion
if(![json objectForKey:#"error"]){
NSLog(#"status %#",[json valueForKeyPath:#"data.status"]);
if([[json valueForKeyPath:#"data.status"]intValue] == 200){
// Everything is oké, and login is succesfull
}else{
//show validation
}
}else {
NSLog(#"Cannot connect to the server");
}
}];*/
This is how I do this in AFnetworking. But what are the differences when I do this in RestKit. I searched for tutorials. But after the update from RestKit 1.0 to 2.0 a lot of these tutorials are outdated. So I hope anybody can help me out with this!
Kind regards!
I used this tutorial for using RestKit. It shows you how to use it and you can learn the other details. http://www.youtube.com/watch?v=dFi9t8NW0oY