AFNetworking - How to make POST request - iphone

EDIT 07/14
As Bill Burgess mentionned in a comment of his answer, this question is related to the version 1.3 of AFNetworking. It may be outdated for the newcomers here.
I'm quite new to iPhone development, and I'm using AFNetworking as my services library.
The API i'm querying is a RESTful one, and I need to make POST requests. To do this, I tried with the following code :
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:#"my_username", #"username", #"my_password", #"password", nil];
NSURL *url = [NSURL URLWithString:#"http://localhost:8080/login"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Pass Response = %#", JSON);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Failed Response : %#", JSON);
}];
[operation start];
There are two main issues with this code :
AFJSONRequestOperation seems to make a GET request, not a POST one.
I can't put parameters to this method.
I also tried with this code :
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:#"my_username", #"username", #"my_password", #"password", nil];
NSURL *url = [NSURL URLWithString:#"http://localhost:8080"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient postPath:#"/login" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Succes : %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure : %#", error);
}];
Is there a better way to make what I want here to get it done ?
Thanks for the help !

You can override the default behavior of your request being used with AFNetworking to process as a POST.
NSURLRequest *request = [client requestWithMethod:#"POST" path:path parameters:nil];
This assumes you have overridden the default AFNetworking setup to use a custom client. If you aren't, I would suggest doing it. Just create a custom class to handle your network client for you.
MyAPIClient.h
#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"
#interface MyAPIClient : AFHTTPClient
+(MyAPIClient *)sharedClient;
#end
MyAPIClient.m
#implementation MyAPIClient
+(MyAPIClient *)sharedClient {
static MyAPIClient *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:webAddress]];
});
return _sharedClient;
}
-(id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
self.parameterEncoding = AFJSONParameterEncoding;
return self;
}
Then you should be able to fire off your network calls on the operation queue with no problem.
MyAPIClient *client = [MyAPIClient sharedClient];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
NSString *path = [NSString stringWithFormat:#"myapipath/?value=%#", value];
NSURLRequest *request = [client requestWithMethod:#"POST" path:path parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// code for successful return goes here
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
// do something with return data
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
// code for failed request goes here
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
// do something on failure
}];
[operation start];

Related

How to wait response and parse XML done in afnetworking iOS

I want to wait until server reponse and parse XML done, then call another function. How can i do that? I used this code to send request to server and use NSXMLParser to parse XML response.
NSURL *url1 = [NSURL URLWithString:#"linkserver"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: url1] ;
NSDictionary *params1 = #{
#"a" : vd;
#"b" : #"all"
};
NSMutableURLRequest *afRequest = [httpClient requestWithMethod:#"GET" path:nil parameters:params1] ;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
NSString * parsexmlinput = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
[self parseXMLFile:parsexmlinput];// parse xml
[self getItemFromStatus];// wait to call another function at here???
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}
];
[httpClient enqueueHTTPRequestOperation:operation];
}
Please give me any suggestion. Thanks much
You have to make your request synchronous.
refer code something like:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/friends/ids.json?"]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
[request setHTTPMethod: #"GET"];
NSError *requestError;
NSURLResponse *urlResponse = nil;
NSData *response1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
check this tutorial Ray Wenderlich using AFnetworking.
Using blocks and callbacks
- (IBAction)xmlTapped:(id)sender{
NSString *weatherUrl = [NSString stringWithFormat:#"%#weather.php?format=xml",BaseURLString];
NSURL *url = [NSURL URLWithString:weatherUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFXMLRequestOperation *operation =
[AFXMLRequestOperation XMLParserRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser) {
XMLParser.delegate = self;
[XMLParser setShouldProcessNamespaces:YES];
[XMLParser parse];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParser) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[NSString stringWithFormat:#"%#",error]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}];
[operation start];
}
You can do it in synchronous way...
NSURLResponse *response = nil;
NSError *error = nil;
NSURL *url = [NSURL URLWithString:urlStr];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
httpClient.parameterEncoding = AFFormURLParameterEncoding;
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:[url path] parameters:params];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString * parsexmlinput = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
if(error) {
errorCallback(error, nil);
} else {
//parse the xml here
}
OR
You can achive it by adding [operation waitUntilFinished],after it's added to the operation queue.Refer this==>Can AFNetworking return data synchronously (inside a block)?
OR
EDIT: In case you don't want to use the AFNetworking library.I prefer this way.
NSString *action_Post =[[NSString alloc] initWithFormat:#"authToken=%#",theMutableString];
NSURL *action_Url =[NSURL URLWithString:#"ur url here"];
NSData *action_PostData = [action_Post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *action_postLength = [NSString stringWithFormat:#"%d", [action_PostData length]];//your parameter to be posted here
NSMutableURLRequest *action_Request = [[NSMutableURLRequest alloc] init];
[action_Request setURL:action_Url];
[action_Request setHTTPMethod:#"POST"];
[action_Request setValue:action_postLength forHTTPHeaderField:#"Content-Length"];
[action_Request setValue:#"application/xml" forHTTPHeaderField:#"Accept"];
//[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[action_Request setHTTPBody:action_PostData];
NSURLResponse *action_response;
NSData *action_Result = [NSURLConnection sendSynchronousRequest:action_Request returningResponse:&action_response error:&error];
if (!action_Result)
{
NSLog(#"Error");
}
else
{
//Parse your xml here
//call ur function
}
Modify it according to your GET/PUT methods.But i suggest you to go for POST method,as it is refered to be as secured one.

AFNetworking not returning data synchronously inside a block

Can't receive JSON synchronously inside a block using AFNetworking. I checked this solution. It
always nil at the end of method.
Here is my method:
- (BOOL)whois:(NSString *)domain withZone: (NSString*) zone
{
__block NSString *resultCode;
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
return YES;
}
return NO;
}
Instead of creating aNSOperationQueue, start your AFJSONRequestOperation with [operation start] and then call [operation waitUntilFinished] and it will block the main thread until it's finished. Then your resultCode should not be nil.
As #mattt said in the post you linked, it is strongly discouraged to freeze the thread like this. Consider figuring out another way to do this, such as calling a new method you hope to continue from your success block, and a different failure method from your failure block.
Your method can't function with its current design.
- (BOOL)whois:(NSString *)domain withZone: (NSString*) zone
{
__block NSString *resultCode;
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
// *** Runs 1st
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// *** runs 3rd
resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
// *** Runs 2nd
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
return YES;
}
return NO;
}
Because the material in the block runs third, and asynchronously, you won't be able to return that value to the greater method in the manner it is currently designed. Perhaps use something like this:
- (void)whois:(NSString *)domain withZone: (NSString*) zone
{
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
__weak id weakSelf = self;
// Runs 1st
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSString *resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
[weakSelf receivedResultCode:resultCode];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
}
- (void) receivedResultCode:(NSString *)resultCode {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
// do #YES stuff
}
else {
// do #NO stuff
}
}
Obviously you'll have to change the design of whoever's calling it because it won't return a value in the way you specified. Perhaps there is a better solution, but I think this is the type of design required for it to work.

No Response from AFJSONRequestOperation

I'm trying to get the AFJSONRequestOperation to work with http://www.crowdkind.org/mobile/offers.
I get through the synchronous part and it returns data and puts it in the table just fine. When I go to convert it to AFJSONRequestOperation - I don't get a response. The operation is sent as the NSLog tells me that. But I never get "Operation succeeded or failed". Any idea what might be happening? Here's my searchBarButtonClicked method...
URLtoSearch is set to... http://www.crowdkind.org/mobile/offers. (feel free to click to as it's valid JSON).
- (void)searchBarSearchButtonClicked
{
searchResults = [NSMutableArray arrayWithCapacity:10];
[queue cancelAllOperations];
isLoading = YES;
[self.tableView reloadData];
NSURL *url = [self urlWithSearchText:URLtoSearch];
NSLog(#"URL: %#", URLtoSearch);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Operation succeeded");
[self parseDictionary:JSON];
[searchResults sortUsingSelector:#selector(compareName:)];
isLoading = NO;
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Operation failed");
[self showNetworkError];
isLoading = NO;
[self.tableView reloadData];
}];
operation.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript", nil];
[queue addOperation:operation];
NSLog(#"Operation added");
}
I get no response. Check the link, it's valid json. Any thoughts greatly appreciated!

How do I set the data for a "PUT" request with AFNetworking?

I have started to use AFNetworking and it works well when it gets to making simple "GET"-request. However now I am trying to do a "POST"-request. I use the code below to do the "GET" request. When looking at the puthPath of AFHTTPClient there is no way to set the data to use for the body. My guess is that there is another way of fixing this. I have been looking at the AFHTTPOperation as a way of fixing this. However I am not getting this to work. The problem is that I do not know how to use it with Basic Authentication.
Could somebody give me a hint of how to do a simple "POST"-request with AFNetworking?
AFHTTPClient* client = [AFHTTPClient clientWithBaseURL:ServerURL];
[client setAuthorizationHeaderWithUsername:self.username
password:self.password];
NSString* resourcePath = [NSString stringWithFormat:#"/some/resource/%#",
endPath];
[client getPath:resourcePath
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Success code omitted
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Some error handling code omitted
}
];
I did not find any easy way to do this. But I did as recommended and created my own sub-class of AFHTTPClient. In the subclass I implemented the methods below. This makes it possible to perform both POST-request & PUT-requests with my own data.
- (void)postPath:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
{
NSURLRequest *request = [self requestWithMethod:#"POST" path:path parameters:parameters data:data];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self enqueueHTTPRequestOperation:operation];
}
- (void)putPath:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
{
NSURLRequest *request = [self requestWithMethod:#"PUT" path:path parameters:parameters data:data];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self enqueueHTTPRequestOperation:operation];
}
-(NSMutableURLRequest*)requestWithMethod:(NSString *)method
path:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data;
{
NSMutableURLRequest* request = [super requestWithMethod:method
path:path
parameters:parameters];
[request setHTTPBody:data];
return request;
}
With AFNetworking 2.0, I just copy code from
- (AFHTTPRequestOperation *)PUT:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
and add a
[request setHTTPBody:data];
Here is it:
NSString* str = [bookDetailLink objectForKey:#"Body"];
NSData* data = [str dataUsingEncoding: NSUTF8StringEncoding];
NSMutableURLRequest *request = [self.manager.requestSerializer requestWithMethod:#"PUT" URLString:bookingDetailUrl parameters:nil error:nil];
[request setHTTPBody:data];
AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *op, NSHTTPURLResponse *response) {
NSLog(#"%#", response);
}
failure:^(AFHTTPRequestOperation *op, NSError *error) {
NSLog(#"%#", error);
}];
[self.manager.operationQueue addOperation:operation];
I am integrating Skyscanner API to our iOS app using AFNetworking.
With AFNetworking 1.3.2 the following code works for me:
NSData *imageData = UIImageJPEGRepresentation(thumb, 0.85F);
AFHTTPClient *httpClient = [[AFHTTPClient alloc]
initWithBaseURL:[NSURL URLWithString:#"https://example.com/"]];
NSMutableURLRequest *request = [httpClient
requestWithMethod:#"PUT" path:#"/foo" parameters:nil];
[request setHTTPBody:imageData];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
AFHTTPRequestOperation *operation = [httpClient
HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *op, NSHTTPURLResponse *response) {
NSLog(#"%#", response);
}
failure:^(AFHTTPRequestOperation *op, NSError *error) {
NSLog(#"%#", error);
}];
[operation start];
This results in a PUT request with correct headers, Content-Lenght and general RESTfulness :-)

Empty JSON returned by AFNetworking on iOS5

I'm trying to load a simple JSON inside my iPhone app using the AFNetworking library. I'm using two different approaches:
NSURL *url = [NSURL URLWithString:#"http://www.mysite.com/test.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Name: %#", [JSON valueForKeyPath:#"name"]);
} failure:^(NSURLRequest* req,NSHTTPURLResponse *req2, NSError *error,id mex) {
NSLog(#"%#", [error description]);
}];
[operation start];
and using AFHttpClient (which would be my favorite,since it has all the features for dealing with my REST API:
NSString *baseurl = #"http://www.mysite.com";
NSString *path = #"/test.json";
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseurl]];
[client registerHTTPOperationClass:[AFJSONRequestOperation class]];
//[client setAuthorizationHeaderWithUsername:#"myusername" password:#"mypassword"];
[client getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
//NSLog(#"sjson: %#", [JSON valueForKeyPath:#"entries"]);
NSLog(#"sjson: %#", JSON);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error Code: %i - %#",[error code], [error localizedDescription]);
}];
now,the problem: in both cases I'm receiving an empty Json. I've tested the server side and the web-service is giving the right response and the right MIME type (application/json).
I've already checked this discussion https://github.com/AFNetworking/AFNetworking/issues/175
but my target is pointing to 5.0 and ARC is not enabled for the AFNetworking library.
Maybe I'm missing something obvious here,but I cannot fix it at the moment.
Thanks a lot!
Claus