I have the following code:
NSDictionary *parameters = #{#"parameter1":objectVariable1,#"parameter2":objectVariable2};
[MANAGER POST:#"http:www.myserver.com/somelink" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//PROCESS RESPONSE
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
//PROCESS FAILURE
}];
In case of failure how do I retry this connection considering the objectVariable1 & objectVariable2 can change at any point (while the connection is sent to the server) thus the same post parameters as before must be sent.
Can the parameters be obtained from AFHTTPRequestOperation *operation in the error branch?
Just to be sure, for example, this can be the case:
- objectVariable1 = 1
- send connection with objectVariable1 = 1
- objectVariable1 = 2
- connection fails and should retry with objectVariable1 = 1
Quick and dirty solution:
-(void) post: (NSString *) post withParams: (NSDictionary *) params andReplies: (NSInteger) replies
{
[MANAGER POST:post parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//PROCESS RESPONSE
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
//PROCESS FAILURE
if (replies > 0) {
[self post: post withParams: params andReplies: replies - 1];
}
}];
}
Related
I have this class that performs a REST call .
- (NSString*)cerca:(NSString*)email{
if ([[Utility sharedUtility] connected]) {
HttpClient *self = [HttpClient sharedHTTPClient];
[self.requestSerializer setValue:[self getUserAgent] forHTTPHeaderField:#"User-Agent"];
[self.requestSerializer setValue:NST_CODE forHTTPHeaderField:#"Nst-Code"];
[self.requestSerializer setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"nst_id"] forHTTPHeaderField:#"Nst-Id"];
[self.requestSerializer setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"api_key"] forHTTPHeaderField:#"Api-Key"];
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[#"email"] = email;
__block NSString *result;
[self POST:#"get_info" parameters:parameters
success:^(NSURLSessionDataTask *task, id responseObject) {
result = responseObject;
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
return result;
}
The call returns a JSON . My problem is that when I do return result ; Nothing is passed to the calling method .
Can you tell me why ??
The calling method is
HttpClient *client = [HttpClient alloc];
NSString *result = [client cerca:email];
That method you are calling is an asynchronous call, meaning the result comes after you have returned. You need to change your method to accept a block argument and return the result in the callback.
- (void)cerca:(NSString*)email callback:(void (^)(id result)) callback {
if ([[Utility sharedUtility] connected]) {
...
[self POST:#"get_info" parameters:parameters
success:^(NSURLSessionDataTask *task, id responseObject) {
if (callback) {
callback(responseObject)
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
}
}
To call the method you would do:
[client cerca:email completion:^(NSString *response) {
// Do what you want with the response.
}];
Its because the self POST call is asynchronous and you are returning the result before it has had time to be assigned. You need to rebuild your - (NSString*)cerca:(NSString*)email method so it somehow can handle the asynchronousness of this. Easiest way is usually to change the method so it takes a completion block as in parameter. Maybe something like:
- (void)cerca:(NSString *)email completion:(void (^)(NSString *res))completion {
[self POST:#"get_info" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if (completion) {
completion(responseObject);
}
}failure:^(NSURLSessionDataTask *task, NSError *error) {
if (completion){
completion(nil);
}
}];
}
I changed my method
- (NSString*)cerca:(NSString*)email completion:(void (^)(NSString *res))completion {
But I do not understand what to call it . Indications ?
[client cerca:email completion:nil];
I'm moving my app code to an MVC model and so I created a method to retrieve some data from an API.
+ (NSMutableArray *)loadFromFeed {
NSString *feed = #"https://api.test.com";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:feedUrl]];
request = [mutableRequest copy];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [JSONResponseSerializerWithData serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *jsonArray = (NSArray *)[responseObject objectForKey:#"items"];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
CLS_LOG(#"Error");
}];
}
Now, ideally, I'd like to return jsonArray as part of this method. However, since AFHTTPRequestOperation is asynchronous, I don't know how to solve this and still be able to call [Data loadFromFeed]; anywhere in the app. How can I do this?
You could pass two block named success and failure to loadFromFeed ,
and then call the two block from your setCompletionBlockWithSuccess success and failure block, passing jsonArray to the success block:
typedef void (^Success)(id data);
typedef void (^Failure)(NSError *error);
- (void)loadFromFeed:(Success)success failure:(Failure)failure;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *jsonArray = (NSArray *)[responseObject objectForKey:#"items"];
success?success(jsonArray):nil;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure?failure(error):nil;
}];
then use in this way:
[Data loadFromFeed:^(id data) {
NSLog(#"%#",data)
} failure:^(NSError *error) {
NSLog(#"%#",error)
}];];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://aaa"
success:^(AFHTTPRequestOperation *operation, id responseJSON) {
...
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[TWMessageBarManager sharedInstance]
showMessageWithTitle:#"Network connection failure"
description:#"Please check your network"
type:TWMessageBarMessageTypeError];
}];
Some block is constant, can be used repeatedly. For example here's failure block, How can I reuse this block for reduce the amount of code?
I hope it is a global reuse, rather than the current context, so I can store it as a property? Or get_method()?
Save the block to a variable, then you can pass that around:
void (^failureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^void(AFHTTPRequestOperation *operation, NSError *error) { /* write what you want */ };
void (^successBlock)(AFHTTPRequestOperation *operation, id responseJSON) = ^void(AFHTTPRequestOperation *operation, id responseJSON) { /* write what you want */ };
Then you can use it in further calls like this:
[manager GET:#"" success:successBlock failure: failureBlock];
Bonus: Check out this guide.
you can save it like a variable like so:
void(^blockname)(AFHTTPRequestOperation*, NSError*) = ^(AFHTTPRequestOperation *operation, NSError *error) {
[[TWMessageBarManager sharedInstance]
showMessageWithTitle:#"Network connection failure"
description:#"Please check your network"
type:TWMessageBarMessageTypeError];
}
then just put blockname for the failure parameter instead of the whole thing
Another approach, instead of reuse blocks, you should consider reuse the whole function
- (void)getURLPath:(NSString *)urlPath withSuccessBlock:(void (^)(id responseJSON))block {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:urlPath
success:^(AFHTTPRequestOperation *operation, id responseJSON) {
...
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[TWMessageBarManager sharedInstance]
showMessageWithTitle:#"Network connection failure"
description:#"Please check your network"
type:TWMessageBarMessageTypeError];
}];
}
I'm trying to create a function that will return a value based on the response from an AFNetworking POST request.
However, I can't figure out a way to do this, as the function is asynchronous, so the value is returned before the response is actually received.
int didLogin;
__block NSString *response;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
NSDictionary *parameters = #{#"username":username, #"password":password};
[manager POST:loginUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
//json "response" object from server response
response = [responseObject objectForKey:#"response"];
NSLog(#"Response: %#", response);
[HUD hide:YES];
[HUD removeFromSuperViewOnHide];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[HUD hide:YES];
[HUD removeFromSuperViewOnHide];
NSLog(#"Login Error: %#", error);
}];
if ([response compare:#"1"] == NSOrderedSame || [response compare:#"2"] == NSOrderedSame)
{
//successful login/1-new device/2-existing device
didLogin = 1;
}
else if ([response compare:#"0"] == NSOrderedSame)
{
//unsuccessful login/invalid password
didLogin = 0;
}
else{
//unsuccessful login
didLogin = 2;
}
At which point it would return didLogin.
Is there any way to make this work, or do I need to use a synchronous request?
Your method should take a callback block as a parameter and then you should call that block with didLogin when the asynchronous process is complete. You need to embrace the asynchronous nature of what you're trying to do throughout your code.
When launching my app it checks if user is subscribed. If it dosent detect an internet connection it crashes with the error:
Failed to retrieve subscription with error 'The Internet connection appears to be offline.' and responseString: (null)*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
.m
[self getPath:path
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![responseObject isKindOfClass:[NSDictionary class]])
{
failureBlock(#"Invalid response received");
return;
}
NSDictionary *responseDict = (NSDictionary *)responseObject;
if (responseDict[#"error"] == nil)
{
[self saveUserDict:responseDict];
successBlock(responseDict);
}
else
{
failureBlock(responseDict[#"error"]);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DebugLog(#"Failed to log in with error '%#' and response: %#", error.localizedDescription, operation.responseString);
failureBlock(#"An unknown error occurred");
}];
}
- (void)getSubscriptionWithSuccessBlock:(void (^)(NSDictionary *subscriptionDict))successBlock
failureBlock:(void (^)(id responseObject))failureBlock
{
static NSString *path = #"/api/subscription";
NSDictionary *parameters = #{
#"userId" : userDict[#"userId"],
#"token" : userDict[#"token"]
};
DebugLog(#"Getting subscription with parameters: %#", parameters);
[self getPath:path
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![responseObject isKindOfClass:[NSDictionary class]])
{
failureBlock(#"Invalid response received");
return;
}
NSDictionary *subscriptionDict = (NSDictionary *)responseObject;
if (subscriptionDict[#"error"] == nil)
{
DebugLog(#"Successfully retrieved subscription");
successBlock(subscriptionDict);
}
else
{
failureBlock(responseObject);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DebugLog(#"Failed to retrieve subscription with error '%#' and responseString: %#", error.localizedDescription, operation.responseString);
id responseObject = [NSJSONSerialization JSONObjectWithData:operation.responseData
options:0
error:nil];
failureBlock(responseObject);
}];
}
You need to check for the error before proceeding with the below line. In case there is an error do not call JSONObjectWithData: method with null data.
[self getPath:path
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![responseObject isKindOfClass:[NSDictionary class]])
{
failureBlock(#"Invalid response received");
return;
}
NSDictionary *responseDict = (NSDictionary *)responseObject;
if (responseDict[#"error"] == nil)
{
[self saveUserDict:responseDict];
successBlock(responseDict);
}
else
{
failureBlock(responseDict[#"error"]);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (!error) {
DebugLog(#"Failed to retrieve subscription with error '%#' and responseString: %#", error.localizedDescription, operation.responseString);
id responseObject = [NSJSONSerialization JSONObjectWithData:operation.responseData
options:0
error:nil];
failureBlock(responseObject);
} else {
//handle the error scenario
failureBlock(#"error occured");
}
}];
check for internet connection with Reachability framework.