AFNetworking http client not sending JSON parameters - ios

I created a subclass of AFHTTPClient and am trying to send some JSON parameters to a server.
However the server is responding with a Expected content type
{(
"text/json",
"application/json",
"text/javascript"
)}, got application/xml
According to AFNetworking FAQ
If you're using AFHTTPClient, set the parameterEncoding property to AFJSONParameterEncoding. Any method on that HTTP client with a parameters argument will now encode the passed object into a JSON string and set the HTTP body and Content-Type header appropriately.
I've done that here but the server appears not to recognize the content-headers. Does anyone know of a potential solution?
Here is the method:
- (void)getCompanyDataWithString:(NSString*)companySearchQuery
finish:(LBMarkitAPIRequestCompletionBlock)finishBlock
{
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setParameterEncoding:AFJSONParameterEncoding];
NSDictionary *params = [NSDictionary dictionaryWithObject:
companySearchQuery forKey:#"input"];
NSMutableURLRequest *searchQueryRequest = [self requestWithMethod:#"GET"
path:kMarkitCompanyURL parameters:params];
AFJSONRequestOperation *searchRequestOperation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:searchQueryRequest
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json)
{
NSLog(#"Response: %#", response);
NSLog(#"JSON: %#",json);
NSMutableArray *results = [NSMutableArray array];
NSError *anError = [[NSError alloc] init];
if ([json objectForKey:#"Message"])
{
NSString *message = [json objectForKey:#"Message"];
anError = [[NSError alloc] initWithDomain:message
code:100
userInfo:nil];
}
// Need some error handling code here
for (id item in json)
{
NSString *aName = [item objectForKey:#"Name"];
NSString *aSymbol = [item objectForKey:#"Symbol"];
NSString *anExchange = [item objectForKey:#"Exchange"];
LBCompany *aCompany = [[LBCompany alloc] initWithName:aName
Symbol:aSymbol Exchange:anExchange];
[results addObject:aCompany];
}
// Need to run the passed in block after JSON
// Request Operation succeeds
finishBlock(results,anError);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response,
NSError *error, id JSON)
{
NSLog(#"request failed: %#",[error localizedDescription]);
NSLog(#"Response: %#",response);
NSLog(#"JSON: %#",JSON);
}];
[searchRequestOperation start];
NSLog(#"JSON operation started");
}

The issue was with URL formatting. I didn't notice an API implementation detail that made sending Query Parameters necessary and also specifying JSON output in the URI.
There were no issues with regard to AFNetworking.

Related

AFNetworking NSData - Incompatible block pointer types sending

I am trying to redo some code to use AFNetworking. I have this method below:
-(NSArray *)GetTableDataOfPhase:(NSString *)phase
{
NSString *phaseRequestString = [NSString stringWithFormat:#"%#?jobNo=%#",kIP,phase];
NSURL *JSONURL = [NSURL URLWithString:phaseRequestString];
NSURLResponse* response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
if(data == nil)
return nil;
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
return tableArray;
}
and right now I am trying to alter it so it still returns an array, I have tried doing this:
-(NSArray *)GetTableDataOfPhase:(NSString *)phase
{
NSString *phaseRequestString = [NSString stringWithFormat:#"%#?jobNo=%#",kIP,phase];
NSURL *JSONURL = [NSURL URLWithString:phaseRequestString];
NSURLResponse* response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData* data = [NSURLConnection sendSynchronousRequest:responseObject returningResponse:&response error:nil];
if(data == nil)
return nil;
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
return tableArray;
}
but I got this error:
/Users/jamessuske/Documents/My Programs/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:168:46: Incompatible block pointer types sending 'void *(^)(AFHTTPRequestOperation *, id)' to parameter of type 'void (^)(AFHTTPRequestOperation *, id)'
and this warning:
/Users/jamessuske/Documents/My Programs/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:170:97: Sending 'NSURLResponse *const *' to parameter of type 'NSURLResponse **' discards qualifiers
This is how I am calling it:
- (void)GetRequest
{
//refresh table view
[dataSource.editedCellHolder removeAllObjects];
[dataSource.cellHolder removeAllObjects];
[dataSource.cellHolderDisplay removeAllObjects];
NSArray *tableData = [dataSource.areaData GetTableDataOfPhase:[NSString stringWithFormat:#"%#%#",areaPickerSelectionString,unitPickerSelectionString]];
if(tableData == nil)
[self CustomAlert:#"Data was not recieved from the server, please check internet/VPN settings, Or contact Software Vendor for assistance."];
[dataSource PopulateTableData:tableData];
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
[loadingView removeFromSuperview];
loadingView = nil;
indicatorView =nil;
[NSTimer scheduledTimerWithTimeInterval:0.2f target:self selector:#selector(DisplayTable) userInfo:nil repeats:NO];
}
A couple of things:
Using AFNetworking, you should entirely lose the NSURLConnection request.
Likewise, the default responseSerializer does the JSON parsing for you, so you can lose the NSJSONSerialization parsing. AFNetworking does all of that for you.
Likewise, don't build URL parameters manually, but rather again let AFNetworking do that for you. By default, AFNetworking uses a requestSerializer that will build the request for you.
Your old method ran synchronously, which is generally a bad idea. Instead, you should use asynchronous patterns (e.g. a completionHandler block).
So, pulling all of this together, it probably looks like:
- (void)getTableDataOfPhase:(NSString *)phase completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"jobNo" : phase};
[manager GET:kIP parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
completionHandler(responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionHandler(nil, error);
}];
}
And you'd call it like so:
[self getTableDataOfPhase:#"..." completionHandler:^(NSArray *resultsObject, NSError *error) {
if (resultsObject) {
// use NSArray here
} else {
NSLog(#"error = %#", error);
}
}];
// but don't try to use the `resultsObject` array here!

AFJSONRequestOperation not working (postpath works)

I am trying to post a JSON using AFNetworking.
Here's the code that im using:
+ (RESTAPI *)sharedClient
{
static RESTAPI *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:#"https://mybaseurl.com"]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self setParameterEncoding:AFJSONParameterEncoding];
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
[self setAllowsInvalidSSLCertificate:YES];
return self;
}
The following code does not works. Everytime i try i get the following error:
The operation couldn’t be completed. (NSURLErrorDomain error -1012.)
// this code does not works
//
- (void)loginNOTWORKING
{
RESTAPI *client = [RESTAPI sharedClient];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
NSDictionary *parameter = #{#"tgout": #"1",
#"tgin": #2,
#"username": #"foo",
#"password":#"bar"};
NSURLRequest *request = [client requestWithMethod:#"POST" path:#"/login" parameters:parameter];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// code for successful return goes here
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
NSLog(#"THIS IS NEVER CALLED: %#", JSON);
// do something with return data
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
// code for failed request goes here
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
NSLog(#"SAD, VERY SAD: %#", error.localizedDescription);
// do something on failure
}];
[operation start];
}
This code works:
// this code WORKS
- (void)loginWORKING
{
RESTAPI *client = [RESTAPI sharedClient];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
NSDictionary *parameter = #{#"tgout": #"1",
#"tgin": #2,
#"username": #"foo",
#"password":#"bar"};
[client postPath:#"/login" parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
NSLog(#"IT WORKS: %#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Response: %#", error.localizedDescription);
}];
}
Why the first login method does not works? What am i doing wrong?
Try by replacing
NSURLRequest *request = [client requestWithMethod:#"POST" path:#"/login" parameters:parameter];
with
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"/*HERE THE URL STRING TO CALL*/"]]
You can find the error -1012 in file CFNetworkErrors.h:
kCFURLErrorUserCancelledAuthentication = -1012
"The connection failed because the user cancelled required authentication."
I guess, there is an issue with your authentication. The error description is possibly misleading with regard to "the user" - it is actually a delegate method that gets invoked which cancels the authentication, or the authentication simply fails.
This of course can be caused by not properly serializing the parameters. I would suggest to use a lower level API, create the request manually, encode the JSON manually with NSJSONSerialization, and set the body data and the URL of the request. IMHO, this is certainly more readable code, and likely requires less code.

Parsing JSON response

I am using AFJSONRequestOperation to request a remote API:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Remove the SVProgressHUD view
[SVProgressHUD dismiss];
//Check for the value returned from the server
NSData *jsonData = [JSON dataUsingEncoding:NSUTF8StringEncoding];//This line cause crash
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
loginDic=[[NSDictionary alloc]init];
loginDic=[arr objectAtIndex:0];
NSLog(#"%#",loginDic);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#", [error.userInfo objectForKey:#"NSLocalizedDescription"]);
}];
[operation start];
[SVProgressHUD showWithStatus:#"Loading"];
However, the app crashes and I am getting this error:
[__NSCFDictionary dataUsingEncoding:]: unrecognized selector sent to instance
Here is an NSLog for the JSON object returned:
Result = (
{
operation = 5;
result = 1;
}
);
Am I missing something, because I think that I am not parsing correctly the JSON object. Please correct me.
It looks like AFJSONRequestOperation is deserializing JSON to a dictionary for you, and then you're trying to do it again. JSON is an NSDictionary but you're calling an NSString method.
Remove all of this code:
NSData *jsonData = [JSON dataUsingEncoding:NSUTF8StringEncoding];//This line cause crash
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
loginDic=[[NSDictionary alloc]init];
loginDic=[arr objectAtIndex:0];
And replace it with:
loginDic = [[JSON objectForKey:#"Result"] lastObject];
(That'll work safely without checking array bounds, but assumes that there's only one element in the array.)
The object you get in the success block is already parsed by AFJSONRequestOperation.
In your case you get a NSDictionary object.
You can check the class of the object using the isKindofClass-method:
if ([JSON isKindOfClass:[NSDictionary class]]) {
NSDictionary* dict = (NSDictionary*)JSON;
...
}

AFHttpclient get json body in block but outer function returns null

I am trying to send post request on some url and in body to be only json data (trying to register new user sending json like
{
"username": "test",
"password": "test",
"email": "email#gmail.com"
}
I have function like
-(NSString*) sendPostOnUrl:(NSString*) url
withParameters:(NSDictionary*)params{
__block NSString* response = nil;
NSError *error;
NSURL *u = [NSURL URLWithString:url];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: u];
[httpClient postPath:REGISTER
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
response = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Request Successful, response '%#'", response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"[HTTPClient Error]: %#", error.localizedDescription);
}];
return response;
}
where params are NSDictionary with keys username, password and email and values for those keys.
Problem is when I send I always in return get null in response (the latest line) but in NSLog I get json response.. I am very new to ios, and it looks to me that I need to sync on some way block with return from function but don't know how, can anybody give me a clue what am I doing wrong ? (params contains all those keys when I try to debug, url is ok, REGISTER is NSString constant)
Blocks are asynchronous - the problem here is that "response = [[NSString alloc] initWithData..." is within the block which gets executed after you've exited the method. A better approach is to not do this in a method, instead place this code where you were calling sendPostOnUrl:withParameters: and do whatever it is you need to do within the success block. So instead of:
self.something = [self sendPostOnUrl:url withParameters:#{"username":"test" etc}];
you do this:
NSError *error;
NSURL *u = [NSURL URLWithString:url];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: u];
__weak YourClassName *me = self;
[httpClient postPath:REGISTER
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
me.something = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Request Successful, response '%#'", response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"[HTTPClient Error]: %#", error.localizedDescription);
}];
Also, take note of "__weak YourClassName *me = self", you cannot reference self within a block because it will cause a retain cycle.

Possible to add property to object in array in blockopertion (AFNetwork)?

I'm making several request from different sources, and because of this I want to add a property like: '"newsSource" = twitter' (JSON format) to the created NSArray resultsTwitter below. The reason is I want be able to handle each "newsitem" uniquely.
I'm new to blocks, but I think it might be an really easy way to do this "on the fly"?
If not possible within the block operation, any suggestion on how to do it after operation is done?
// Fetch data from Twitter (json complient)
NSURLRequest *request = [NSURLRequest requestWithURL:urlTwitter];
AFJSONRequestOperation *operation;
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *req, NSHTTPURLResponse *responce, id jsonObject) {
NSLog(#"Responce: %#",jsonObject);
self.resultsTwitter = [jsonObject objectForKey:#"results"];
[self.tableView reloadData];
}
failure:^(NSURLRequest *req, NSHTTPURLResponse *responce, NSError *error, id jsonObject) {
NSLog(#"Recieved an HTTP %d", responce.statusCode);
NSLog(#"The error was: %#",error);
}];
[operation start];
I may not have understood your question correctly, but as long as resultsTwitter is a NSMutableArray, you can add an object (in your case an NSDictionary with a single KVP) after it is initially populated.
Something like:
[resultsTwitter addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"twitter", #"newsSource",
nil]];
Example of instantiating a variable that can be accessed inside a block:
__block NSString *newssource = #"";
NSURLRequest *request = [NSURLRequest requestWithURL:urlTwitter];
AFJSONRequestOperation *operation;
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *req, NSHTTPURLResponse *responce, id jsonObject) {
NSLog(#"Responce: %#",jsonObject);
self.resultsTwitter = [jsonObject objectForKey:#"results"];
[self.tableView reloadData];
newssource = #"twitter";
}
failure:^(NSURLRequest *req, NSHTTPURLResponse *responce, NSError *error, id jsonObject) {
NSLog(#"Recieved an HTTP %d", responce.statusCode);
NSLog(#"The error was: %#",error);
}];
[operation start];
Create a Model class to encapsulate the behavior of all News Items.
This pattern is used in the AFNetworking example app, with each App.net post corresponding to a model object, which is initialized from JSON. I would strongly recommend against using a mutable dictionary rather than a model object as a means of representing items.

Resources