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.
Related
I develop an app and I use the AFNetworking to comunicate with the server.Everything is oky except one call which return status code: 500; second time when I call the WS is oky.
My method :
-(IBAction)quizzSubmit:(id)sender {
BusinessLogic *bl = [BusinessLogic sharedManager];
[AlertDFF alertWithTitle:#"Loading ..." owner:self];
NSString *plm = [NSString stringWithFormat:#"[{\"nid\":%d,\"score\":%d,\"time\":%d,\"options\":{\"cid1\":\"%#\",\"cid2\":\"%#\",\"cid3\":\"%#\",\"cid4\":\"%#\",\"cid5\":\"%#\",\"cid6\":\"%#\",\"cid7\":\"%#\",\"cid8\":\"%#\",\"cid9\":\"%#\",\"cid10\":\"%#\"}}]", self.quizzId, self.score, self.time, [self.quizzAnswersDic objectForKey:#"cid1"], [self.quizzAnswersDic objectForKey:#"cid2"], [self.quizzAnswersDic objectForKey:#"cid3"], [self.quizzAnswersDic objectForKey:#"cid4"], [self.quizzAnswersDic objectForKey:#"cid5"], [self.quizzAnswersDic objectForKey:#"cid6"], [self.quizzAnswersDic objectForKey:#"cid7"], [self.quizzAnswersDic objectForKey:#"cid8"], [self.quizzAnswersDic objectForKey:#"cid9"], [self.quizzAnswersDic objectForKey:#"cid10"]];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
bl.currentUser.hash, #"hash",
plm,#"quizzes",
nil];
ApiClientBlitzApp *client = [ApiClientBlitzApp sharedClient];
NSURLRequest *request = [client requestWithMethod:#"POST" path:getQuizSubmit parameters:params];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest
*request, NSHTTPURLResponse *response, id resultObjectsJSON)
{
if (resultObjectsJSON count]>0){
[AlertDFF dismissAlertInController:self];
[Utils showAlert:#“Your Quiz Result was sent ! ” andDelegate:nil];
}else{
[AlertDFF dismissAlertInController:self];
[Utils showAlert:#“Problems ! Please try again later! ” andDelegate:nil];
}
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"ERROR response = %# /n", response);
NSLog(#"error = %#", error);
[AlertDFF dismissAlertInController:self];
}];
[operation start];
} }
The WS team told me that my request doesn't reah to the server.
If I press second time the button and call again the metod everthing it's oky.
I need some help with this problem please.
I test the request on Chrome - Postman and everything it's oky.
This is the method I use to do HTTP GET request. How can I change it to use HTTPS?
AS_NET_BASE_URL = http://www.myapp.com
Is it just as simple as replacing the http:// with https:// ?
Thank you in advance
+(void)startGETRequestAtUrlRoute:(NSString *)route withParameters:(NSString *)slashSeparatedParams completion:(void (^)(BOOL, id))completion{
//If internet is rachable, start request
if ([self isInternetReachable]) {
//Where request is going
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:AS_NET_BASE_URL]];
NSString *path = [NSString stringWithFormat:#"%#%#%#", AS_NET_BASE_URL, route, slashSeparatedParams];
//Tell operation to expect JSON
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
//Start spinner
[self startNetworkIndicator];
//Set up actual GET request
[httpClient getPath:path
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Stop spinning
[self stopNetworkIndicator];
//Make the response JSON valid
if (responseObject) {
completion(YES, responseObject);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self stopNetworkIndicator];
completion(NO, NULL);
//Error
NSLog(#"%#", error);
}
];
//No internet connection
}else{
completion(NO, NULL);
}
}
Yes.
HTTP/HTTPS is determined by the scheme of the URL, which in your case is specified in AS_NET_BASE_URL.
I have the following code inside a class (static method) which I call to get data from an API. I decided to make this a static method just so I can reuse it on some other parts of the app.
+ (NSArray*) getAllRoomsWithEventId:(NSNumber *)eventId{
NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:#"http://blablba.com/api/Rooms/GetAll/e/%#/r?%#", eventId, [ServiceRequest getAuth]]];
NSMutableArray *rooms = [[NSMutableArray alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Response of getall rooms %#", JSON);
NSArray *jsonResults = (NSArray*)JSON;
for(id item in jsonResults){
Room* room = [[Room alloc]init];
if([item isKindOfClass:[NSDictionary class]]){
room.Id = [item objectForKey:#"Id"];
room.eventId = [item objectForKey:#"EventId"];
room.UINumber = [item objectForKey:#"RoomUIID"];
[rooms addObject:room];
}
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"Error");
}];
[operation start];
[operation waitUntilFinished];
return rooms;
}
Now my issue is, whenever I call this in a ViewController (ViewDidLoad method). The static method will run till the end and will return null on the rooms, but the Nslog will display the "Success" block Nslog a few seconds after. Now I understand that this is asynchronous so it doesn't wait for the success block to execute before it reaches the "return rooms;" line. With all that said, I need some advice as to how to handle this, like maybe a progress bar or something like that? Or something that delays it? I'm not really sure if that's the reight way or if it is, I am not sure how to do it.
Any advice is very much appreciated. Thank you!
AFNetworking is built around asynchronicity—starting a request, and then executing some piece of code once that request has finished.
waitUntilFinished is an anti-pattern, which can block the user interface.
Instead, your method should have no return type (void), and have a completion block parameter that returns the serialized array of rooms:
- (void)allRoomsWithEventId:(NSNumber *)eventId
block:(void (^)(NSArray *rooms))block
{
// ...
}
See the example app in the AFNetworking project for an example of how to do this.
You can write your method following way:
+ (void) getAllRoomsWithEventId:(NSNumber *)eventId:(void(^)(NSArray *roomArray)) block
{
NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:#"http://blablba.com/api/Rooms/GetAll/e/%#/r?%#", eventId, [ServiceRequest getAuth]]];
NSMutableArray *rooms = [[NSMutableArray alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Response of getall rooms %#", JSON);
NSArray *jsonResults = (NSArray*)JSON;
for(id item in jsonResults){
Room* room = [[Room alloc]init];
if([item isKindOfClass:[NSDictionary class]]){
room.Id = [item objectForKey:#"Id"];
room.eventId = [item objectForKey:#"EventId"];
room.UINumber = [item objectForKey:#"RoomUIID"];
[rooms addObject:room];
}
}
block(rooms);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"Error");
block(nil); //or any other error message..
}];
[operation start];
[operation waitUntilFinished];
}
you can call this method like followings:
[MyDataClass getAllRoomsWithEventId:#"eventid1":^(NSArray *roomArray) {
NSLog(#"roomArr == %#",roomArray);
}];
I'm new to using blocks in iOS and I am thinking that's probably the crux of my problem.
I just want to build a simple static DataManager class whose sole job is to fetch data from my Restful service.
I would call this from all my various UIViewControllers (or collectionview/table controllers)
In my class i have a function that looks like this
+ (NSArray *) SearchByKeyword: (NSString*) keyword {
__block NSArray* searchResults = [[NSArray alloc] init];
NSString *baseURL = #"http://someURL.com/api/search";
NSString *requestURL = [baseURL stringByAppendingString:keyword];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:requestURL
parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
searchResults = [JSON valueForKeyPath:#""];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
return searchResults;
}
However, this keeps returning zero data. Can someone suggest the right way of doing this?
You are trying to use the results of an asynchronous task (the JSON operation) as the return value for a synchronous method call, so that is why you get no data.
You could provide your view controllers with an API that takes completion blocks and failure blocks, similar to the AF networking one. View controllers can then do what they need to do with the results when they are passed into the block.
Modifying your code from your question:
typedef void (^SearchCompletionBlock)(NSArray *results);
typedef void (^SearchFailureBlock)(NSError *error);
+ (void)searchByKeyword:(NSString*)keyword completionBlock:(SearchCompletionBlock)completionBlock failureBlock:(SearchFailureBlock)failureBlock;
{
NSString *baseURL = #"http://someURL.com/api/search";
NSString *requestURL = [baseURL stringByAppendingString:keyword];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:requestURL
parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if (completionBlock) {
completionBlockc([JSON valueForKeyPath:#""]);
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
if (failureBlock) {
failureBlock(error);
}
}];
[operation start];
}
Then clients could pass completion blocks that stored the results and reloaded their views. Something like:
^ (NSArray *results) {
self.results = results;
[self.tableView reloadData];
}
Your JSON request operation is asynchronous, meaning that it will kick off the request ([operations start], then immediately return your results, which will be empty. When the completion block runs, it assigns your data but nothing is done with it. Your search method can't return an object unless it waits for the request to complete.
You've got a few options:
Pass in a completion block to the search method which does something with the results. The completion block is called in the completion block of the request, once all the service-specific stuff (processing JSON etc) is finished. (Block inception!)
Have the completion block of the request assign a property of the data manager, then call a delegate method or notification to let others know the results are available.
I'd prefer option 1.
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.