I am using AFNetworking in this example but I think it pertains more to NSOperation. I have two operations, once is dependent on the other finishing. However op2 really shouldn't run until op1's success block has completely run. In the case of dependencies in an operation queue, op2 will run as soon as op1 is done, but before op1's success block is done.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSURLRequest *request = [manager.requestSerializer requestWithMethod:#"GET" URLString:url parameters: nil error: nil];
NSOperation *op1 = [http.manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id userLocations) {
NSLog(#"Success");
// do some stuff
// more stuf
// I am done, ready for the next operation.
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
NSOperation* op2 = // create op, this will depend on op1 finishing
[op2 addDependency:Op1]; // op2 is dependent on op1 finishing
[manager.operationQueue addOperations:#[op1, op2] waitUntilFinished:NO];
This does not quite work for me as op2 is dependent on some things that are set in op1's success block. Meaning op2 cannot start until op1 is done with its success block.
Is there a way with NSOperations to queue them such that each can wait until the blocks are done running as well? If not how can I re-design tho accomplish this dependency.
I would structure things a bit differently, setting up the second operation within the first. Like this:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSURLRequest *request = [manager.requestSerializer requestWithMethod:#"GET" URLString:url parameters: nil error: nil];
NSOperation *op1 = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id userLocations) {
NSLog(#"Success");
// do some stuff
// more stuf
// I am done, ready for the next operation.
// SO put the other operation here!
NSOperation* op2 = // create op, this will depend on op1 finishing
[manager.operationQueue addOperation:op2];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[manager.operationQueue addOperation:op1];
I run in to the same problem and found out an excellent solution with just using [operationQueue setSuspended:YES] please see the second answer in this post: NSOperation wait until asynchronous block executes
Related
I have the following setup that uses AFNetworking to make calls to my server. I have used an example I found on the internet to include a completion block so I know when the call has finished.
File "FCEngine.m"
- (void)fetchBusinessProfile:(NSString *)userID userAccessToken:(NSString *)userAccessToken completion:(void (^)(NSDictionary *json, BOOL success))completion {
/// Validate the user token again the user id.
NSDictionary *parameters = [[NSDictionary alloc]initWithObjectsAndKeys:userAccessToken,#"user_access_token",
userID,#"user_id",
nil];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
manager.requestSerializer = serializer;
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager POST:#"" parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON Business Profile: %#", responseObject);
fetchBusinessProfileCompletion(responseObject, YES);
} failure:^(NSURLSessionTask *operation, NSError *error) {
//NSLog(#"Error: %#", error);
NSMutableDictionary *errorResponse = [[NSMutableDictionary alloc] init];
[errorResponse setObject:#"connection_error" forKey:#"state"];
[errorResponse setObject:[error localizedDescription] forKey:#"description"];
fetchBusinessProfileCompletion(errorResponse, YES);
}];
}
- (void)fetchNotifications:(NSString *)userID userAccessToken:(NSString *)userAccessToken completion:(void (^)(NSDictionary *json, BOOL success))completion {
/// Validate the user token again the user id.
NSDictionary *parameters = [[NSDictionary alloc]initWithObjectsAndKeys:userAccessToken,#"user_access_token",
userID,#"user_id",
nil];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
manager.requestSerializer = serializer;
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager POST:#"" parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
//NSLog(#"JSON: %#", responseObject);
completion(responseObject, YES);
} failure:^(NSURLSessionTask *operation, NSError *error) {
//NSLog(#"Error: %#", error);
NSMutableDictionary *errorResponse = [[NSMutableDictionary alloc] init];
[errorResponse setObject:#"connection_error" forKey:#"state"];
[errorResponse setObject:[error localizedDescription] forKey:#"description"];
completion(errorResponse, YES);
}];
}
The following is how I make the call on Main View Controller
- (void)MyMethods {
[self.fcEngine fetchBusinessProfile:userID userAccessToken:userAccessToken completion:^(NSDictionary *json, BOOL success) {
/// Response here
}];
[self.fcEngine fetchNotifications:self.userID userAccessToken:self.userAccessToken completion:^(NSDictionary *json, BOOL success) {
//// Response here
}];
}
Now the problem is that the 2 calls are made one after another and when I fetch the data for one e.g. "fetchBusinessProfile" the competition block on both is called.
Have I set this up wrong? If 2 or more calls I only want the completion to be called for that particular block and not them all.
I don't think you understand asynchronous as well as completion blocks. If you make the 2 network calls as defined above, they can happen in any order. The completion in fetchBusinessProfile and fetchNotifications will be different completion blocks ... unless you make them the same.
For example:
[self.fcEngine fetchBusinessProfile:userID userAccessToken:userAccessToken completion:^(NSDictionary *json, BOOL success) {
/// Handle response
// Note calling the SAME completion block
sameCompletionBlockAlreadyDefined();
}];
[self.fcEngine fetchNotifications:self.userID userAccessToken:self.userAccessToken completion:^(NSDictionary *json, BOOL success) {
//// Handle response
// Note calling the SAME completion block
sameCompletionBlockAlreadyDefined();
}];
In this case, sameCompletionBlockAlreadyDefined() is some already defined block. In this case, the body of the block of each call is indeed, but funnel to the same call via sameCompletionBlockAlreadyDefined. It is possible you are confused because completion happens to be named the same in your first snippet.
Note your question is really poorly phrased so it isn't fully clear on what you mean.
The larger question is what is your goal? Do you want only one completion block to be called at the end? Or you want fully distinct completion blocks? Both require different techniques. Be clear on what your goal is.
The former would be best service with a dispatch_group. The latter requires different completion blocks.
An example of dispatch group would be something like:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self.fcEngine fetchBusinessProfile:userID userAccessToken:userAccessToken completion:^(NSDictionary *json, BOOL success) {
/// Handle response
dispatch_group_leave(group);
];
self.fcEngine fetchNotifications:self.userID userAccessToken:self.userAccessToken completion:^(NSDictionary *json, BOOL success) {
//// Handle response
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// This would be some completion block which means all is done
completion();
I have this bool method that returns a yes or no for an inputted string.
I'm successfully able to return a YES or a NO, but I cannot seem to able to make a network connection and return a YES or a NO depending on the server's response.
I tried using __block and I don't feel like that will wait for the web request to finish, is there a way to return YES or NO in the success block without it giving me the error:
Incompatible block pointer types sending 'BOOL(^)(NSURLSessionTask*__strong, NSError __strong' to parameter of the type 'void(^)(NSURLSessionTask...)
-(BOOL)customResponseForString:(NSString *)text {
__block BOOL response_available;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObject:#"text/plain"]];
[manager GET:[NSString stringWithFormat:#"http://example.com/response.php?input=%#", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
response_available = (BOOL)response[#"response_available"];
if (response_available) {
[session sendTextSnippet:response[#"response"] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
} else {
response_available = NO;
}
[session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
[session sendRequestCompleted];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//return NO;
}];
});
return response_available;
}
Your block definition syntax is probably erroneous, because you can definitely return a BOOL along other parameters in a block.
- (void)fetchCurrentUserWithCompletion:(void (^)(BOOL success, User *user))completion;
This method would be called like this:
[self.userProfileController fetchCurrentUserWithCompletion:^(BOOL success, User *user) {
if (success) {
NSLog(#"Current User Name: %#", user.fullName);
}
}];
If you use AFNetworking, check the AFHTTPRequestOperation object that handle completionBlocks:
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
User *user = [self userFromResponseObject:responseObject];
if (completion) completion(YES, user);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completion) completion(NO, user);
}];
Because you are implicitly initializing response_available to NO and then using an async GCD call, your method as written will always immediately return NO without waiting for the request to finish. Note: switching to dispatch_sync won't help either because AFNetworking will queue the GET request asynchronously either way.
Best Approach
Add a completion block argument to customResponseForString:. Then simply execute your completion block in the success or failure blocks of the AFHTTPRequestOperation.
Workable Approach (use caution!)
It is possible to make customResponseForString: wait for a response to the network request, but you will have significant issues if it is ever called from the main thread.
First you create a dispatch group and tell it you are starting some long-running work:
dispatch_group_t networkGroup = dispatch_group_create();
dispatch_group_enter(networkGroup);
Then you need to make your network request and when it completes tell the group that the work is finished with dispatch_group_leave():
[manager GET:[NSString stringWithFormat:#"http://example.com/response.php?input=%#", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
response_available = (BOOL)response[#"response_available"];
if (response_available) {
[session sendTextSnippet:response[#"response"] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
} else {
response_available = NO;
}
[session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
[session sendRequestCompleted];
dispatch_group_leave(networkGroup);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
response_available = NO;
dispatch_group_leave(networkGroup);
}];
Before your original method returns, tell it to wait for the entire group to finish processing:
dispatch_group_wait(networkGroup, DISPATCH_TIME_FOREVER);
return response_available;
You could adjust this time interval as needed or leave it at DISPATCH_TIME_FOREVER to let the network request time out on its own.
I know there is another similar question, but it's for an older version of AFNetworking, and doesn't really answer it anyway.
I have the following code:
AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
__block NSDictionary* response = nil;
AFHTTPRequestOperation* operation = [manager
GET: #"https://10.20.30.40:8765/foobar"
parameters: [NSDictionary dictionary]
success:^(AFHTTPRequestOperation* operation, id responseObject){
response = responseObject;
NSLog(#"response (block): %#", response);
}
failure:^(AFHTTPRequestOperation* operation, NSError* error){
NSLog(#"Error: %#", error);}
];
[operation waitUntilFinished];
NSLog(#"response: %#", response);
...
If I run this, what I'll see in my log is:
2013-12-09 09:26:20.105 myValve[409:60b] response: (null)
2013-12-09 09:26:20.202 myValve[409:60b] response (block): {
F00005 = "";
F00008 = "";
F00013 = "";
}
The NSLog that is after the waitUntilFinished fires first. I expected it to fire second. What am I missing?
A couple of thoughts:
The issue is that waitUntilFinished will wait for the core network operation to complete, but it will not wait for the success or failure completion blocks. If you want to wait for the completion blocks, you can use a semaphore:
__block NSDictionary* response = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
AFHTTPRequestOperation* operation = [manager GET: #"https://10.20.30.40:8765/foobar"
parameters: [NSDictionary dictionary]
success:^(AFHTTPRequestOperation* operation, id responseObject){
response = responseObject;
NSLog(#"response (block): %#", response);
dispatch_semaphore_signal(semaphore);
}
failure:^(AFHTTPRequestOperation* operation, NSError* error){
NSLog(#"Error: %#", error);
dispatch_semaphore_signal(semaphore);
}];
NSLog(#"waiting");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// [operation waitUntilFinished];
NSLog(#"response: %#", response);
You can, alternatively, wrap this in your own concurrent NSOperation subclass, posting isFinished in the AFHTTPRequestOperation completion blocks, eliminating the semaphore in the process.
Note, make sure to specify completionQueue if doing semaphores on the main queue
because, in the absence of that, AFNetworking defaults to dispatching completion handlers to the main queue and you can deadlock.
As an aside, you should never block the main queue (poor UX, your app could be killed by watchdog process, etc.), so if you're doing this from the main queue, I'd discourage the use of either waitUntilFinished or the semaphore. It's better to just initiate whatever you need from within the completion blocks, letting the main queue continue execution while this asynchronous network operation is in progress, e.g.:
[activityIndicatorView startAnimating];
AFHTTPRequestOperation* operation = [manager GET: #"https://10.20.30.40:8765/foobar"
parameters: [NSDictionary dictionary]
success:^(AFHTTPRequestOperation* operation, id responseObject){
// do whatever you want with `responseObject` here
// now update the UI, e.g.:
[activityIndicatorView stopAnimating];
[self.tableView reloadData];
}
failure:^(AFHTTPRequestOperation* operation, NSError* error){
// put your error handling here
// now update the UI, e.g.:
[activityIndicatorView stopAnimating];
}];
// NSLog(#"waiting");
// dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// // [operation waitUntilFinished];
// NSLog(#"response: %#", response);
It sounds like you want to let your model let the UI do any necessary updates when the model object is done doing its updates. So, you can use your own block parameters so that the view controller can tell the model object what to do when its done (instead of using waitUntilFinished or semaphore to make the network operation block the main queue). For example, let's assume your model had some method like this:
- (void)updateModelWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failure
{
AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
AFHTTPRequestOperation* operation = [manager GET: #"https://10.20.30.40:8765/foobar"
parameters: [NSDictionary dictionary]
success:^(AFHTTPRequestOperation* operation, id responseObject){
// do your model update here
// then call the success block passed to this method (if any),
// for example to update the UI
if (success)
success();
}
failure:^(AFHTTPRequestOperation* operation, NSError* error){
NSLog(#"Error: %#", error);
// if caller provided a failure block, call that
if (failure)
failure(error);
}];
}
Then your view controller can do something like:
[modelObject updateModelWithSuccess:^{
// specify UI updates to perform upon success, e.g.
// stop activity indicator view, reload table, etc.
} failure:^(NSError *error){
// specify any UI updates to perform upon failure
}]
Bottom line, your code can use the same style of completion blocks that AFNetworking uses. If you want the model to pass information back, you can add additional parameters to the completion blocks, themselves, but I presume the above illustrates the basic idea.
The following code using AFNetworking 2.0 is valid to fetch data through internet:
NSString *URLPath = #"http://www.raywenderlich.com/downloads/weather_sample/weather.php?format=json";
NSDictionary *parameters = nil;
[[AFHTTPRequestOperationManager manager] GET:URLPath parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure: %#", error);
}];
But I want to test those requests synchronously in the unit test. But it would be blocked when using GCD semaphore like this:
// This code would be blocked.
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSString *URLPath = #"http://www.raywenderlich.com/downloads/weather_sample/weather.php?format=json";
NSDictionary *parameters = nil;
[[AFHTTPRequestOperationManager manager] GET:URLPath parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", responseObject);
dispatch_semaphore_signal(sema);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure: %#", error);
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release_ARC_compatible(sema);
How can I fetch data using AFNetworking 2.0 library synchronously (test those code in Kiwi)?
Your semaphore would be blocked because by default AFNetworking runs on the main loop. So if you're waiting on the main loop for the semaphore, AFNetworking's code never gets to run.
In order to fix this, you simply have to tell AFNetworking to use a different dispatch queue. You do that by setting the operationQueue property on AFHTTPRequestOperationManager
You can create your own dispatch queue, or use one of the predefined ones, like so:
// Make sure that the callbacks are not called from the main queue, otherwise we would deadlock
manager.operationQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
So I'm rewriting an app for iOS 7 with AFNetworking 2.0 and I'm running into the issue of sending a batch of requests at once and tracking their progress. In the old AFNetworking there was the enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock: method on AFHTTPClient, this is clearly refactored out and I'm a bit confused on how to enqueue multiple requests.
I have created a subclass of AFHTTPSessionManager and I'm using the POST:... and GET:... methods to communicate with the server. But I can't find anything in the code and/or docs to enqueue multiple requests at once like with the old AFHTTPClient.
The only thing I can find is the undocumented batchOfRequestOperations:progressBlock:completionBlock: method on AFURLConnectionOperation, but that looks like the iOS 6 way of doing this.
Clearly I'm missing something in the new NSURLSession concept that I should use to batch requests or looking over a new AFNetworking feature. Hope someone can help me on the right track here!
tl;dr: How can I send a batch of requests with my AFHTTPSessionManager subclass?
Thanks Sendoa for the link to the GitHub issue where Mattt explains why this functionality is not working anymore. There is a clear reason why this isn't possible with the new NSURLSession structure; Tasks just aren't operations, so the old way of using dependencies or batches of operations won't work.
I've created this solution using a dispatch_group that makes it possible to batch requests using NSURLSession, here is the (pseudo-)code:
// Create a dispatch group
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 10; i++) {
// Enter the group for each request we create
dispatch_group_enter(group);
// Fire the request
[self GET:#"endpoint.json"
parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
// Leave the group as soon as the request succeeded
dispatch_group_leave(group);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
// Leave the group as soon as the request failed
dispatch_group_leave(group);
}];
}
// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do whatever you need to do when all requests are finished
});
I want to look write something that makes this easier to do and discuss with Matt if this is something (when implemented nicely) that could be merged into AFNetworking. In my opinion it would be great to do something like this with the library itself. But I have to check when I have some spare time for that.
Just updating the thread... I had the same problem and after some researches I found some good solutions, but I decided to stick with this one:
I am using the project called Bolts. So, for the same sample above posted by #Mac_Cain13, it would be:
[[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
BFTask *task = [BFTask taskWithResult:nil];
for (int i = 0; i < 10; i++) {
task = [task continueWithBlock:^id(BFTask *task) {
return [self executeEndPointAsync];
}];
}
return task;
}] continueWithBlock:^id(BFTask *task) {
// Everything was executed.
return nil;
}];;
- (BFTask *) executeEndPointAsync {
BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
[self GET:#"endpoint.json" parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
[task setResult:responseObject];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
[task setError:error];
}];
}];
return task.task;
}
Basically, it's stacking all of the tasks, waiting and unwrapping until there is no more tasks, and after everything is completed the last completion block is executed.
Another project that does the same thing is RXPromise, but for me the code in Bolts was more clear.
For request which can be post or get, you can use AFNetworking 2.0 for batch operation as firstly you need to create operation like this:
//Request 1
NSString *strURL = [NSString stringWithFormat:#"your url here"];
NSLog(#"scheduleurl : %#",strURL);
NSDictionary *dictParameters = your parameters here
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:#"POST" URLString:strURL parameters:dictParameters error: nil];
AFHTTPRequestOperation *operationOne = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationOne = [AFHTTPResponseSerializer serializer];
[operationOne setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//do something on completion
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#",[error description]);
}];
//Request 2
NSString *strURL1 = [NSString stringWithFormat:#"your url here"];
NSLog(#"scheduleurl : %#",strURL);
NSDictionary *dictParameters1 = your parameters here
NSMutableURLRequest *request1 = [[AFHTTPRequestSerializer serializer] requestWithMethod:#"POST" URLString:strURL1 parameters:dictParameters1 error: nil];
AFHTTPRequestOperation *operationTwo = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
operationTwo = [AFHTTPResponseSerializer serializer];
[operationTwo setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//do something on completion
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#",[error description]);
}];
//Request more here if any
Now perform batch operation like this :
//Batch operation
//Add all operation here
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:#[operationOne,operationTwo] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations)
{
NSLog(#"%i of %i complete",numberOfFinishedOperations,totalNumberOfOperations);
//set progress here
yourProgressView.progress = (float)numberOfFinishedOperations/(float)totalNumberOfOperations;
} completionBlock:^(NSArray *operations)
{
NSLog(#"All operations in batch complete");
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
On AFNetworking 2.0, AFHTTPClient has been split on AFHTTPRequestOperationManager and AFHTTPSessionManager, so probably you could start with the first, which has operationQueue property.
Currently, NSURLSession tasks are not suitable for the same kind of patterns request operations use. See the answer from Mattt Thompson here regarding this issue.
Direct answer: if you need dependencies or batches, you'll still need to use request operations.