Hello I want to get result from asynchronous block from the class DataViewControllerto use it in the class MagazineViewControllerbut I don't get the value of returnedDataunless the ViewDidLoadmethod is called for the second time or more as the operation is asynchronous, I know that I have to implement a completion block and call it but I don't know exactly how to do it, this is my code, how can I edit it to give me the value returned in the setCompletionBlockWithSuccessblock and use it in other classes
DataViewController.m
- (void)getDataFromServer:(NSString *)urlString completion:(void (^)(AFHTTPRequestOperation *operation, id responseObject))completion {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSURLCredential *credential = [NSURLCredential credentialWithUser:userName password:Password persistence:NSURLCredentialPersistenceNone];
NSMutableURLRequest *request = [manager.requestSerializer requestWithMethod:#"GET" URLString:urlString parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:credential];
[operation setResponseSerializer:[AFJSONResponseSerializer alloc]];
[operation setCompletionBlockWithSuccess:completion failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
} ];
[manager.operationQueue addOperation:operation];
}
The MagazineViewController class (Where I want to use the returnedData)
#implementation MagazineViewController
void (^completion)(AFHTTPRequestOperation* , id) = ^(AFHTTPRequestOperation *operation, id responseObject){
returnedData = responseObject;};
- (void)viewDidLoad {
[super viewDidLoad];
DataViewController *dataViewController = [[DataViewController alloc] init];
[dataViewController getDataFromServer:#"http://firstluxe.com/api/search/search?query=vogue&language=1&output_format=JSON" completion:completion];
NSLog(#"returned %# ", returnedData); // here I get the value after the view controller is loaded for the second time or more
You should define completion inline. Your first NSLog statement is called before completion has been called, so the returnedData variable has not been set.
#implementation MagazineViewController
- (void)viewDidLoad {
[super viewDidLoad];
DataViewController *dataViewController = [[DataViewController alloc] init];
[dataViewController getDataFromServer:#"http://firstluxe.com/api/search/search?query=vogue&language=1&output_format=JSON"
completion:^ (AFHTTPRequestOperation *operation, id responseObject) {
returnedData = responseObject;
NSLog(#"returned %# ", returnedData);
}];
}
#end
[self getDataFromServer:#"API URL" completion:^(int *operation, id responseObject) {
// Result from async method here.
}];
Related
My scene is like this, first I have a server json api which return some data for specify page, the api is like /data/page/1. For this case, suppose the response data is :
page 1 => ['a','b']
page 2 => ['c','d']
page 3 => ['e','f']
I use AFNetworking 2 to fetch data from api, for single page data request it works well.
The problem is now I want to implement parallel request for more than one page. I need one api for view controller which accept one pages number, and callback with all data for these pages collected. The api I need is:
typedef void (^DataBlock)(id data);
- (void) dataForPages:(NSInteger)pages withSuccessBlock:(DataBlock)block;
If view controller pass 3 for pages parameter, I want AFNetworking can request data parallel and then collected the 3 result then use in callback block.
I tried to use NSOperationQueue to process multi AFHTTPRequestOperation but failed, the code demo is like this:
- (void) dataForPages:(NSInteger)pages withSuccessBlock:(DataBlock)block
{
//want to use for each, here suppose pages is 3
NSMutableArray *result = [[NSMutableArray alloc] init];
AFHTTPRequestOperation *op1 = [[AFHTTPRequestOperation alloc] initWithRequest:#"/data/page/1"];
AFHTTPRequestOperation *op2 = [[AFHTTPRequestOperation alloc] initWithRequest:#"/data/page/2"];
AFHTTPRequestOperation *op3 = [[AFHTTPRequestOperation alloc] initWithRequest:#"/data/page/3"];
[op1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[result addObjectsFromArray: responseObject]; //responseObject is ['a', 'b']
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[op2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[result addObjectsFromArray: responseObject]; //responseObject is ['c', 'd']
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[op3 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[result addObjectsFromArray: responseObject]; //responseObject is ['e', 'f']
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
NSOperationQueue *q = [[NSOperationQueue alloc] init];
[q addOperation:op1];
[q addOperation:op2];
[q addOperation:op3];
[q waitUntilAllOperationsAreFinished];
block(result);
}
In my test the result always empty, I'm not quite understand waitUntilAllOperationsAreFinished.
Anyone knows how to deal this problem with NSOperation or GCD?
After some code research, I found it's difficult to get what I want with NSOperation and NSOperationQueue, because AFNetworking has it's own completion block handler.
The final solution is use dispatch_group, all code is like this:
dispatch_group_t group = dispatch_group_create();
NSURLRequest *req1 = ...;
NSURLRequest *req2 = ...;
AFHTTPRequestOperation *op1 = [[AFHTTPRequestOperation alloc] initWithRequest:req1];
AFHTTPRequestOperation *op2 = [[AFHTTPRequestOperation alloc] initWithRequest:req2];
NSMutableArray *result = [[NSMutableArray alloc] init];
dispatch_group_enter(group); //enter group
[op1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[result addObjectsFromArray: responseObject];
dispatch_group_leave(group); //leave group in completion handler
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[op2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[result addObjectsFromArray: responseObject];
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
dispatch_group_leave(group);
}];
[op1 start];
[op2 start];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
block(result);
});
I am making a simple GET request using AFNetworking 2, but I am getting a NSURLErrorDomain error.
I created a manager class which subclasses AFHTTPRequestOperationManager and creates a singleton instance so that I can use a shared manager.
+ (id)manager {
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
NSURL *baseURL = [ZSSAuthentication baseURL];
self = [super initWithBaseURL:baseURL];
if (self) {
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:[ZSSAuthentication username] password:[ZSSAuthentication password]];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
}
return self;
}
- (void)getData:(NSString *)pubID parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *results))completion failure:(void (^)(NSError *error))failure {
NSString *url = [NSString stringWithFormat:#"data/all/%#", pubID];
[self GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if there are errors
ZSSError *error = [self errorForAPICall:responseObject status:[operation.response statusCode]];
if (error) {
[self logMessage:error.localizedDescription];
failure(error);
return;
}
NSDictionary *data = [responseObject objectForKey:#"data"];
completion(data);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
}
Then, in my viewController's viewDidLoad method I make a call to that method:
[[ZSSManager manager] getData:self.pubID parameters:nil completion:^(NSDictionary *results) {
self.items = results;
[self dataWillReload];
NSLog(#"%#", results);
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error: %# %li", error, (long)error.code);
}];
Then I get this error:
Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)" UserInfo=0x7ff952306610 {NSErrorFailingURLKey=http://test.mysite.com/v1/data/all/5}
The strange thing is, on a previous viewController, I make a different call to the manager, and it completes and returns data correctly. But, when I make this second call, I get the error. AND, if I move that getData call out of the viewDidLoad method, and invoke it with a button press, it DOES WORK. What the heck?
What could be causing this?
i am really new to IOS development. i want to develop an application which is dealing with some web services and display in a table view. somehow i found a 3rd party library for do the networking stuffs [AFNetworking 2]. below is my code to get the json response for any given url and parameters.
-(NSDictionary*)getWebServiceResponce:(NSString *)url :(NSDictionary *)object
{
// NSDictionary *parameters = [[NSDictionary alloc] initWithObjectsAndKeys:#"47", #"caregiverPersonId", nil];
__block NSDictionary* result=Nil;
__block NSString* person=Nil;
AFSecurityPolicy *policy = [[AFSecurityPolicy alloc] init];
[policy setAllowInvalidCertificates:YES];
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager setSecurityPolicy:policy];
operationManager.requestSerializer = [AFJSONRequestSerializer serializer];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager POST:url
parameters:object
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", [responseObject description]);
person = [responseObject[#"d"]objectForKey:#"PersonId"];
// [self returnedResponce:responseObject];
result = (NSDictionary *) responseObject[#"d"];
NSLog(#"RESULT: %#", result);
NSLog(#"personm: %#", person);
[operation waitUntilFinished];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error description]);
//result = [error];
}
];
return result;
}
this code works perfectly.. but my point is when i put some breakpoints to check what are the values i got for several variables, it shows null. but my log shows the entire json response.
and i want to return my response object as a dictionary. because i want to do some process with the response.. can some one help me with this ?
The problem is that result is nil when it gets returned. AFNetworking uses ObjC's awesome blocks, they get executed asynchronously. Read more about it here.
You should include a callback block in your getWebServiceResponce method. I've thrown together a bit of code but you should really read more about blocks.
-(void)webServiceResponceForURL:(NSString *)url dictionary:(NSDictionary *)object success:(void (^)(NSDictionary *responseObject))success {
// NSDictionary *parameters = [[NSDictionary alloc] initWithObjectsAndKeys:#"47", #"caregiverPersonId", nil];
__block NSDictionary* result=Nil;
__block NSString* person=Nil;
AFSecurityPolicy *policy = [[AFSecurityPolicy alloc] init];
[policy setAllowInvalidCertificates:YES];
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager setSecurityPolicy:policy];
operationManager.requestSerializer = [AFJSONRequestSerializer serializer];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager POST:url
parameters:object
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", [responseObject description]);
person = [responseObject[#"d"]objectForKey:#"PersonId"];
// [self returnedResponce:responseObject];
result = (NSDictionary *) responseObject[#"d"];
NSLog(#"RESULT: %#", result);
NSLog(#"personm: %#", person);
//We are executing the block as soon as we have the results.
if (success) {
success(responseObject);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error description]);
//result = [error];
}
];
}
Edit:
[self webServiceResponceForURL:#"foo://foo" dictionary:nil success:^(NSDictionary *responseObject) {
//your code here
}
[self webServiceResponceForURL:#"foo://foo" dictionary:nil success:^(NSDictionary *responseObject) {
//your code here
}
Here you will got complete responseObject in form NSDictionary. You can assign responseObject to instance variable. Now This instance Variable will be used at point time. in your case, it will passed on button event.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I create block like this:
1) Define your own completion block,
typedef void(^myCompletion)(BOOL);
2) Create a method which takes your completion block as a parameter,
-(void) myMethod:(myCompletion) compblock{
//do stuff
compblock(YES);
}
3)This is how you use it,
[self myMethod:^(BOOL finished) {
if(finished){
NSLog(#"success");
}
}];
How can I send array in block and then get new array from block?
//here I get array of image id's and go in loop for download it all,
NSString *URLString = [NSString stringWithFormat: #"%#", requestString];
NSURL * url = [NSURL URLWithString:URLString];
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
userWithImage = [responseObject copy];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image error: %#", error);
}];
[requestOperation start];
//here I save it to mutable array and send as completion block,
yep, I think it will be better to send 1 image id and return in block 1 image. And in method there I will call the block - make action with photo separately. so, Is it possible to do?
I can do something like this with NSNotifications, but it will be more widely when it can be in blocks..
1) Define your own completion block
typedef void(^myCompletion)(BOOL finished, NSArray *myArray);
2) Create a method which takes your completion block as a parameter,
-(void)myMethod:(myCompletion)compblock {
//do stuff
NSArray *myArray = ...;
compblock(YES, myArray);
}
3)This is how you use it,
[self myMethod:^(BOOL finished, NSArray *myArray) {
if (finished){
NSLog(#"success");
}
}];
If you just want to write a wrapper around the AFNetworking request you could write a method like this:
- (void)downloadImageWithPath:(NSString *)path completion:(void (^)(AFHTTPRequestOperation *operation, UIImage *image, NSError *error))completion __attribute__((nonnull(2)));
{
NSParameterAssert(completion);
NSURL *url = [NSURL URLWithString:path];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(operation, responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(operation, nil, error);
}];
[requestOperation start];
}
You would invoke this with something like:
[self downloadImageWithPath:#"http://url/to/image.jpg"
completion:^(AFHTTPRequestOperation *operation, UIImage *image, NSError *error) {
if (error) {
// handle error
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI with image
});
}];
I'm trying to make a get request to a restful service. This request has worked for the last 3 weeks when I was using a request without a request manager. As soon as I added a request manager because I wanted to send a dictionary of parameters, the code keeps crashing with the error:
'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: urlRequest'
My code is as follows. The commented out code still works if I uncomment it and replace the new code with it. Why am I able to make a request from that URL but not with AFNetworking?
NSString *URLForSend = [NSString stringWithFormat:#"%#%#/", ([_remoteDownloadURLString stringByAppendingString:_getFilesJsonURLString]),_userName ];
NSURL *URL = [NSURL URLWithString:URLForSend];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
self.downloadJsonRequest = [[AFHTTPRequestOperation alloc] initWithRequest:request];//this works to create a a afhttprequestoperation, with the same url
_downloadJsonRequest.responseSerializer =
[AFJSONResponseSerializer serializerWithReadingOptions: NSJSONReadingMutableContainers];
__unsafe_unretained typeof(self) weakSelf = self;
NSMutableDictionary *mutDictForSend = [[NSMutableDictionary alloc] initWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:#"tokenInfo"]];
[mutDictForSend removeObjectForKey:#"success"];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init];
[manager GET:URLForSend parameters:mutDictForSend success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"%#", responseObject);
weakSelf.jsonArrayForCaching = responseObject;
[[NSNotificationCenter defaultCenter] postNotificationName:kBIDContentEndingSync object:weakSelf];
[weakSelf gotNewJson:responseObject];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Request Failure Because %#",[error userInfo]);
}];
/*
[_downloadJsonRequest setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"%#", responseObject);
weakSelf.jsonArrayForCaching = responseObject;
[[NSNotificationCenter defaultCenter] postNotificationName:kBIDContentEndingSync object:weakSelf];
[weakSelf gotNewJson:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Request Failure Because %#",[error userInfo]);
}];
[[NSNotificationCenter defaultCenter] postNotificationName:kBIDContentStartingSync object:self];
[_downloadJsonRequest start];
*/
I'd suggest replacing the line:
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init];
With
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
The latter calls initWithBaseURL, which does some configuration of the AFHTTPRequestOperationManager, that just calling [[AFHTTPRequestOperationManager alloc] init] does not appear to do.
If you're still having issues even doing that, I'd suggest adding Exception Breakpoint to identify precisely where this exception is taking place.
OK, this was a tough one, but it turns out that requestOperationManager is a singleton so I should not have initialised him, but call its instance...