I have multiple operations (they're AFNetworking requests) with completion blocks that takes some time to execute, and a Core Data object that needs to be saved at the end of all the requests.
MyCoreDataObject *coreDataObject;
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute1 = responseObject;
sleep(5);
}];
[operation1 start];
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute2 = responseObject;
sleep(10);
}];
[operation1 operation2];
[context save:nil];
Of course, this does not work as I want because the requests are asynchronous. I tried adding an NSOperationQueue like so:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:2];
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute1 = responseObject;
sleep(5);
}];
[operationQueue addOperation:operation1];
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute2 = responseObject;
sleep(10);
}];
[operationQueue addOperation:operation2];
[imageQueue waitUntilAllOperationsAreFinished];
[context save:nil];
This looks a bit better. Using waitUntilAllOperationsAreFinished, my queue blocks the current thread until my requests are finished, but not until my success Blocks are finished, which is really what I need.
Any ideas on how to achieve this in a good way?
Use dispatch groups.
dispatch_group_t group = dispatch_group_create();
MyCoreDataObject *coreDataObject;
dispatch_group_enter(group);
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute1 = responseObject;
sleep(5);
dispatch_group_leave(group);
}];
[operation1 start];
dispatch_group_enter(group);
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute2 = responseObject;
sleep(10);
dispatch_group_leave(group);
}];
[operation2 start];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[context save:nil];
AFNetworking has designed method for this kind of operations, which abstracts from GCD:
-enqueueBatchOfHTTPRequestOperationsWithRequests:progressBlock:completionBlock:
-enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:
Take a look at FAQ
I belive something like this:
NSMutableArray *mutableOperations = [NSMutableArray array];
for (NSURL *fileURL in filesToUpload) {
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:fileURL name:#"images[]" error:nil];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[mutableOperations addObject:operation];
}
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:#[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(#"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
NSLog(#"All operations in batch complete");
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
refering to docs: http://cocoadocs.org/docsets/AFNetworking/2.5.0/
My requirements were those of do many request from an array of String (URL)
func updateSourceData(element: Int) {
if element > availableUrls.count - 1 {
return
}
let service = SourceDataServiceDao()
let currentUrl = availableUrls[element]
service.fooCall(url: currentUrl, completion: { (response, error) -> Void in
self.updateSourceData(element + 1)
})
}
Obviously, in this way calls are made in cascade, not N asynchronous calls.
Related
I want to use the response object of AFHTTPRequestOperation outside of the success block. Here is my code below:
NSMutableArray *arr = [[NSMutableArray alloc] init];
NSURL *URL = [NSURL URLWithString:#"my_url"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"user" password:#"pass" persistence:NSURLCredentialPersistenceNone];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setCredential:credential];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
for(NSDictionary *item in responseObject){
[arr addObject:item];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[[NSOperationQueue mainQueue] addOperation:op];
NSLog(#"%#",arr);// here arr getting null or empty
I am getting the response object fine but if I want to use responseObject outside of the success block then object gets null.
Can anybody help me?
How can I run multiple requests inside the success block of 1 request and wait for it to finish?
[manager GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%# Response: \n%#", url, responseObject);
resultsArray = [[NSMutableArray alloc] init];
for (NSDictionary *json in [responseObject objectForKey:#"items"]) {
[self getDetails:json];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[SVProgressHUD dismiss];
}];
Where in getDetails:(id)json is the method to load the group of requests whose parameters are based on the result of the main request.
For example:
I would like to request from the API a list of students, then on the success block. For each student, I would like to get the related data from another table (another request) and put them on my NSObject.
EDIT Here is my getDetails method
- (AFHTTPRequestOperation *)getDetails:(NSDictionary *)json
{
NSLog(#"Start Op %#",[json objectForKey:#"related_salon"]);
NSString *url = [NSString stringWithFormat:#"%#read/salons/%#",SERVER_API_URL,[json objectForKey:#"related_salon"]];
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:req];
//op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success %#",[json objectForKey:#"name"]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed Op %#",error.localizedDescription);
}];
//AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:req];
//op.responseSerializer = [AFJSONResponseSerializer serializer];
[op start];
return op;
}
The AFNetworking GET method returns a ATHTTPRequestOperation (an NSOperation subclass). You could have your getDetails method return that object. You can then create a new operation dependent upon those operations, which you'd run at the end:
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// add here whatever you want to perform when all the getDetails calls are done,
// e.g. maybe you want to dismiss your HUD when all the requests are done.
[SVProgressHUD dismiss];
}];
[manager GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%# Response: \n%#", url, responseObject);
resultsArray = [[NSMutableArray alloc] init];
for (NSDictionary *json in [responseObject objectForKey:#"items"]) {
NSOperation *operation = [self getDetails:json];
[completionOperation addDependency:operation];
}
[[NSOperationQueue mainQueue] addOperation:completionOperation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[SVProgressHUD dismiss];
}];
Again, this is presuming that getDetails is doing its own GET call, and that you change getDetails to (a) capture the NSOperation returned by GET and (b) return it.
I'm trying to create POST request in AFNetworking 2.0:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
AFHTTPRequestOperation *operation = [manager POST: requestURL parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
But how I can create AFHTTPRequestOperation without executing it immediately?
( I need to add this operation to NSOperationQueue )
UPDATE:
Thanks! I ended up with such solution:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod: #"POST" URLString: #"website" parameters: params error: nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start]; // or add operation to queue
Straight from the docs
NSURL *URL = [NSURL URLWithString:#"http://example.com/foo.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", responseObject);
} failure:nil];
[operation start];
Edit
If you want to submit this operation to NSOperationQueue, you can use AFHTTPRequestOperationManager as follows
requestManager = [AFHTTPRequestOperationManager manager];
// instead of [operation start]
[requestManager.operationQueue addOperation:operation];
The best way to add something to an NSOperationQueue is to subclass NSOperation.
There are tutorials all over the place... First example I found with a Google search.
You subclass might be called MyPostOperation.
And it has the single task of running your POST request.
It then gets managed and run by your NSOperationQueue.
Try this
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer]
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", responseObject);
} failure:nil];
// Add the operation to a queue
// It will start once added
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
I am (finally) using afnetworking for my iOS project. I have managed with success to send POST data and an image to my php server, but in separated blocks of code.
Send Data:
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:myname,#"name", mysencondname, #"twoname", nil];
NSURL *url = [NSURL URLWithString:#"http://DOMAIN.COM"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient defaultValueForHeader:#"Accept"];
[httpClient postPath:#"post.php" parameters:parameters
success:^(AFHTTPRequestOperation *operation, id response) {
NSLog(#"operation hasAcceptableStatusCode: %d", [operation.response statusCode]);
[self requestDone:[operation.response statusCode]];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error with request");
NSLog(#"%#",[error localizedDescription]);
[self requestFailed];
}];
Send Image:
NSData *imageToUpload = UIImagePNGRepresentation(_imageView.image);
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://www.DOMAIN.com"]];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST" path:#"upload.php" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData: imageToUpload name:#"avatar" fileName:fullFilename mimeType:#"image/png"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [operation responseString];
NSLog(#"response: [%#]",response);
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self dismissViewControllerAnimated:YES completion:NULL];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if([operation.response statusCode] == 403){
NSLog(#"Upload Failed");
[MBProgressHUD hideHUDForView:self.view animated:YES];
return;
}
NSLog(#"error: %#", [operation error]);
}];
My question is how can I combine them so I can send an image and some data like (myname, #"name", mysencondname, #"twoname") to my server with a single request ?
Inside the multipartFormRequestWithMethod method there is parameter called parameters that you can use to send some data along.
For example:
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
myName, #"name"
, secondName, #"twoname"
, nil];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST" path:#"upload.php" parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData: imageToUpload name:#"avatar" fileName:fullFilename mimeType:#"image/png"];
}];
Here's the problem :
I want to upload image immediately after finish choosing image from imagepicker.
So, I put my code to upload image using afnetworking in imagepicker delegate method.
But it doesn't response anything.
//set the imageview to current image after choosing
profilePic.image = image;
//dismiss the imagepicker
[picker dismissModalViewControllerAnimated:YES];
//start upload image code
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"steventi1901", #"username",
UIImagePNGRepresentation(profilePic.image), #"profile_pic",
nil];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:updateProfile];
NSData *imageToUpload = UIImagePNGRepresentation(profilePic.image);
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"PUT" path:#"" parameters:params
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData: imageToUpload name:#"file" fileName:#"temp.png" mimeType:#"image/png"];
//NSLog(#"dalem");
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [operation responseString];
NSLog(#"response: [%#]",response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if([operation.response statusCode] == 403){
NSLog(#"Upload Failed");
return;
}
NSLog(#"error: %#", [operation error]);
}];
[operation start];
//end upload image code
It really didn't response anything so i didn't know if the process fail or success.
Best and Easy way to upload Image using Afnetworking.
Please use below AFNetworking.
pod 'AFNetworking', '~> 2.5.4'
//Create manager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//parameters if any
NSMutableDictionary *AddPost = [[NSMutableDictionary alloc]init];
[AddPost setValue:#"Addparma" forKey:#"param"];
NSString * url = [NSString stringWithFormat:#"www.addyourmainurl.com"];;
NSLog(#"AddPost %#",AddPost);
[manager POST:url parameters:[AddPost copy] constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
//add image data one by one
for(int i=0; i<[Array count];i++)
{
UIImage *eachImage = [Array objectAtIndex:i];
NSData *imageData = UIImageJPEGRepresentation(eachImage,0.5);
[formData appendPartWithFileData:imageData name:[NSString stringWithFormat:#"image%d",i] fileName:[NSString stringWithFormat:#"image%d.jpg",i ] mimeType:#"image/jpeg"];
}
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
have you set the delegate of the picker?
[yourPicker setDelegate:self]