AFNetworking: getPath:parameters-method causes an issue - ios

I´m trying to do a GET request to my web service, using AFNetworking(great framework). This is the code for the request:
AFHTTPClient *httpClient = [[AFHTTPClient alloc]initWithBaseURL:[NSURL URLWithString:#"http://mywebservice.com/service/"]];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
NSMutableURLRequest *request = [httpClient getPath:#"http://mywebservice.com/service/contacts"
parameters:#{#"accessID":self.accessId, #"name":contactName}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Success code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Error code
}];
This causes the following issue to appear(pointing at my NSMutableURLRequest instance):
Initializing 'NSMutableURLRequest *__strong' with an expression of incompatible type 'void'
I have no idea what causes this, so any help would be appreciated.

This method on AFHTTPClient:
-(void)getPath:parameters:success:failure:
returns nothing (void) and you are trying to assign it to a variable.
NSMutableURLRequest *request = [httpClient getPath:....
This is all the information we need to interpret your error message:
Initializing 'NSMutableURLRequest *__strong' with an expression of incompatible type 'void'
You declare a variable, request, and you type it as NSMutableURLRequest *. For memory management ARC adds the memory semantic __strong. The full type of your variable is `NSMutableURLRequest *__strong. You then attempt to assign, =, the result of the method -(void)getPath:parameters:success:failure: to that variable. The method returns nothing, otherwise known as void. void and NSMutableRequest * are not the same type so the compiler complains with the above error message.
This method actually starts performing the request when you call it. The results are supplied as parameters to the completion or failure block, which are executed when the HTTP request completes. This would be the correct way to perform the HTTP request you are trying to send:
[httpClient getPath:#"contacts"
parameters:#{#"accessID":self.accessId, #"name":contactName}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Success code
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Error code
}];
Notice how this is not assigned to a variable. Also when using these path methods on AFHTTPClient we dont need to include the baseURL, only the parts of the path we want to append on to it.

Related

Reskit response is just a string, not JSON

In my app i am making different calls and they work except one call, that returns just a string in response as SUCCESS. I am getting this error
"The operation couldn’t be completed. (Cocoa error 3840.)", NSLocalizedDescription=Loaded an unprocessable response (200) with content type 'application/json'}
How can i tell the restkit to access the "Content-Type: text/plain"
This is a post call.
Short answer: you can't. RestKit is designed to work with JSON objects only, and so it expects a JSON response (keeping with the RESTful paradigm).
However, you can definitely post objects using AFNetworking, which RestKit actually includes. I use AFNetworking for non-coreData-related correspondence. Here's a code sample on how to get the AFHTTPClient from RestKit and make a POST, expecting a text/plain response.
AFHTTPClient *httpClient = [RKObjectManager sharedManager].HTTPClient;
NSDictionary *requestObject = #{#"label1":data1, #"label2":data2};
[httpClient setParameterEncoding:AFJSONParameterEncoding];
[httpClient setDefaultHeader:#"Accept" value:#"text/plain"];
[httpClient postPath:urlPath parameters:requestObject success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = (NSString*)responseObject;
if([response isEqualToString:#"SUCCESS"]) NSLog(#"It worked!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure code goes here
}];
If that's your only call expecting text/plain, change the Accept header back after you're done:
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];

method execute failure block ios

I have been using AFHTTPClient class' postPath:parameters:success:failure: method. but it always call failure block only. I have been using AFNetworking 0.10 library. Can anyone tell me the possible reasons for this.
following is the code:
NSURL *nsUrl = [NSURL URLWithString:kURLAuthLogin];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:nsUrl];
[httpClient postPath:nil parameters:loginParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"\n\n\n\n\n\n login success \n\n\n\n\n\n");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"\n\n\n\n\n\n login failed \n\n\n\n\n\n");
}];
There is no any problem with nsUrl or any other variable for sure.
What happened is that your login failed.
It would be common sense to
NSLog (#"%#", error);
to get the information you are looking for.
did you made your NSString to support NSURL with adding percentage.
example:-
NSString *loginCredential=[#"Your Url with login and password" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

Multiple AFHTTPClient Request

I've got subclass of AFHTTPClient
The main idea is that i call all API through my singleton of AFHTTPClient subclass, and all requests goes through 1 points for error handling and HUD displaying.
This is entry point for every API calls:
-(void) makeRequestWithPath:(NSString*) path andParams:(NSDictionary*) params
success:(void (^)( id JSON, AFHTTPRequestOperation *operation)) success
failure:(void (^)( NSError *error)) failure
And i've got many methods for API calls something like that:
-(void) getListMainTreeWithSuccess:(void (^)( id JSON, AFHTTPRequestOperation *operation)) success
failure:(void (^)( NSError *error)) failure
{
[self makeRequestWithPath:#"objects/selectlist" andParams:nil success:^(id JSON, AFHTTPRequestOperation *operation) {
success(JSON,operation);
} failure:^(NSError *error) {
failure(error);
}];
}
This works just fine for my needs. But i faced problem that i need to make serial request in loop through my AFHTTPClient subclass and make some action when all of them are finished , I found method
-(void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)urlRequests
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock
which should solve my issue, but the problem is that i call all methods through AFHTTPClient and it's methods getPath: and postPath: and previous way forces me to rewrite everything and makes my subclass completely useless, because I need to add there NSArray of AFHTTPRequestoperation, which is not possible to construct or extract from my subclass and my methods. Previously i tried to use __block 's to synchronise requests with semaphore and something else but i failed to get what i need, please help me!
UPDATE:
It seems that it is not possible to even use enqueueBatchOfHTTPRequestOperations method (even with rewriting all my code) because this method needs array of http request operations, but it's not possible to construct POST request with them.
I solved this with an increment/decrement pending download system and tied the HUD to that.
[networkStatus beginNetworkActivity];
[client someRESTActionWithCompletion:^(id object, NSError *error) {
[networkStatus endNetworkActivity];
if (error) {
// Handle the error ...
}
if (![networkStatus hasNetworkActivity]) {
// All downloads have finished
}
}];
I keep the network status object separate which from the AFHTTPClient subclass, but it can be built into the client if that's what you want.
Network status keeps an internal counter. -beginNetworkActivity increments the counter, if the counter was 0, then it displays a HUD. -endNetworkActivity decrements the counter, if the counter becomes 0, then it dismisses the HUD. -hasNetworkActivity returns YES if the counter greater than 0.
Other Notes: I combine the success and failed callbacks into a single completion callback. I keep the network status logic separate from the client because sometime I'll use a singleton network status object, sometimes I'll use a created instance, sometimes I won't use one at all. It all depends on the needs to the higher level logic.
Again, as #MikePollard said, create AFHTTPRequestOperation using
[AFHHTPClient HTTPRequestOperationWithRequest:success:failure:]
For this method create NSURLRequest using (or use another one, pick which one is suitable for you). Here you can also specify, which method to use POST, GET or any other.
[AFHTTPClient requestWithMethod:
path:
parameters:]
After that save all operation to an NSArray, and schedule them using:
[AFHTTPClient enqueueBatchOfHTTPRequestOperationsWithRequests:
progressBlock:
completionBlock:]
Code example:
NSMutableArray *ops = [NSMutableArray new];
NSMutableURLRequest *request1 = [[AFHTTPClient sharedClient] requestWithMethod:#"GET"
path:#"MyEndpoint"
parameters:#{#"key1": #"value"}];
AFHTTPRequestOperation *op1 = [[AFHTTPClient sharedClient] HTTPRequestOperationWithRequest:request1
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success!");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure!");
}];
[ops addObject:op1];
NSMutableURLRequest *request2 = [[AFHTTPClient sharedClient] requestWithMethod:#"POST"
path:#"MyAnotherEndpoint"
parameters:#{#"key2": #(104)}];
AFHTTPRequestOperation *op2 = [[AFHTTPClient sharedClient] HTTPRequestOperationWithRequest:request2
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success!");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure!");
}];
[ops addObject:op2];
[[AFHTTPClient sharedClient] enqueueBatchOfHTTPRequestOperationsWithRequests:ops
progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(#"numberOfFinishedOperations: %d totalNumberOfOperations %d",
numberOfFinishedOperations,
totalNumberOfOperations);
}
completionBlock:^(NSArray *operations) {
NSLog(#"All operation compelted!");
}];

How do I return a variable from a block inside a method?

Say I have this method that given a URL returns a UIImage:
- (void)getUIImageFromURL:(NSURL *)URL {
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *imageOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
imageOperation.responseSerializer = [AFImageResponseSerializer serializer];
[imageOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
return (UIImage *)responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[imageOperation start];
}
But it keeps giving me this error:
Incompatible block pointer types sending 'UIImage *(^)(AFHTTPRequestOperation *__strong, _strong id)' to parameter of type 'void (^)(AFHTTPRequestOperation *_strong, __strong id)'
I'm somewhat new to blocks, so perhaps I'm approaching this completely backwards. How best would I implement a method like this?
You cannot return an image from inside a block. This is an asynchronous API and cannot be used in the manner you are attempting to. Either use a blocking API, where the method is blocking until the image is downloaded (a bad solution), or implement support for the asynchronous API. For instance, pass a completion block to your getImage method and call it with in the completion block of the download operation. In this block, do what you need with the image.
The callback block in setCompletionBlockWithSuccess: is a void block, you can't change it's return type.
In your case, you would probably set the image inside of your block, instead of returning an image.
[imageOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
self.myImage.image = [UIImage imageWithData:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
However, if you are dealing with AFNetworking and images, there are category methods that should greatly simplify retrieval and cacheing.
[self.myImage setImagewithURL:URL];
You are calling an asynchronous method with two blocks for handling success and failure. By the time one of these handlers is called, your calling method is long gone. It doesn't make any sense to think you could return data to it, because it is gone.
In the success block and failure block, you give instructions what to do when the operation has succeeded or failed. There is nobody there to return anything to. What you do is add code in the block to process and store the result of success in the right place, or to handle errors in the correct way.

Downloading a binary file with RestKit 0.20.x

I have seen a lot of major changes with the RestKit framework in version 0.20.x for the iOS platform.
The one thing I haven't found so far on the web is an example of how to download a binary file with the new version of RestKit.
I need to send a JSON object to a REST service and expect a binary file in return. Would seem simple, wouldn't it but for some reason RestKit only expects JSON (and the common internet content types such as XML) to come back.
The JSON object essentially is a request object telling the service which image it should go and get for me.
Fortunately I have managed to use the underlying AFNNetworking framework to help me with this and leverage the RestKit serializer to produce the request object I needed.
MyRequestClass *request = // ... get my request class instance
RKObjectManager *manager = [RKObjectManager sharedManager];
NSMutableURLRequest *downloadRequest = [manager requestWithObject:request method:RKRequestMethodPOST path:ROUTE_URL_MY_SERVICE parameters:nil];
AFHTTPRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:downloadRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Use my success callback with the binary data and MIME type string
callback(operation.responseData, operation.response.MIMEType, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Error callback
callback(nil, nil, error);
}];
[manager.HTTPClient enqueueHTTPRequestOperation:requestOperation];

Resources