iOS: manage a AFHTTPRequestOperationManager - ios

In my app I should download some JSON files, then I store these URL in a plist as you ca see in my code. After I create an 'AFHTTPRequestOperationManager' and I create a loop where I add some operation for the numbers of my 'url_list'.
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"url_json" ofType:#"plist"];
NSArray *url_list = [NSArray arrayWithContentsOfFile:plistPath];
self.manager = [AFHTTPRequestOperationManager manager];
for (id element in url_list){
NSURL *url = [NSURL URLWithString:element];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFHTTPResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[self.manager.operationQueue addOperation:op];
}
Now this code should be fine, but I want to have two information:
what's the way to know the progress value of my 'manager'?, because I want to know the state of all operation in a single progress value
I want to know when an operation finish, because when an operation finish I should pass 'responseObject' to a method that parse this data
Can you help me?

Take a look at AFNetworking batching documentation:
https://github.com/AFNetworking/AFNetworking#batch-of-operations
It gives you an option to assign progress block which is called on single operation completion and on top of that you can assign completion block which will be called when all operations are completed.
If you need you can still assign completion block to single operation to parse responseObjects.

Related

AFNetworking caching Image without displaying it in ImageView in Swift

I understand that AFNetworking has a function that caches an image as it is loaded to an ImageView.
However, I want to cache the Image without displaying it in an ImageView and I was unable to find any functions for that.
Use [NSBundle mainBundle] resourcePath] to get cache path then use NSFileManager and write file/object/doc etc.
Something like (Objective C not Swift - but you get the idea)
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[self savePNGImage:responseObject withFilename:imageUrl];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", [error description]);
}];
[requestOperation start];
Implement savePNGImage to save your image locally.

AFNetworking and downloading images

I want to know is there way to download images with my code one by one ? Now async
NSString *urlString = link;
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) {
completion(operation, responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[RequestAPI sharedInstance].downloadsCount -= 1;
DDLogError(#"FAIL download image: %#",error);
}];
[requestOperation start];
Try this solution
Create a NSObject class for downloading the images. Lets call this DownloadImages. The class has an array of images to be downloaded arrayOfImagesURL.
Create a delegate for the class DownloadImages to make a call back when you finish downloading an image. View how to this in detail here.
Implement AFNetworking as you like, you can also accomplish this by using the iOS 7 API NSURLSession. So in the completion block of your download task, you need to do 2 things:
Continue downloading the next URL in arrayOfImagesURL.
Do a call back with the delegate to the parent controller for processing.
Tell me if there's something need to be more clarify.

AFNetworking 2.0 download multiple images with completion

I'm trying to figure out a way to download multiple images with AFNewtorking 2.0. I've read a lot of posts here in SO, but can't find the answer I'm looking for, hope you guys can help me.
The problem is that I want to know when all of the downloads finished and if all images where downloaded.
So I have an array with image URL's ant trying to do something like this.
for(NSString *photoUrlString in self.photos){
NSURL *url = [NSURL URLWithString:photoUrlString];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image error: %#", error);
}];
[requestOperation start];
}
I've found some answers with putting these requests into a queue and setting max concurrent operations to 1. But don't know how that works really.
Any help is appreciated, thanks in advance!
for(Photo *photo in array){
//form the path where you want to save your downloaded image to
NSString *constPath = [photo imageFullPath];
//url of your photo
NSURL *url = [NSURL URLWithString:photo.serverPath];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
op.responseSerializer = [AFImageResponseSerializer serializer];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:constPath append:NO];
op.queuePriority = NSOperationQueuePriorityLow;
[op setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead){
}];
op.completionBlock = ^{
//do whatever you want with the downloaded photo, it is stored in the path you create in constPath
};
[requestArray addObject:op];
}
NSArray *batches = [AFURLConnectionOperation batchOfRequestOperations:requestArray progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
} completionBlock:^(NSArray *operations) {
//after all operations are completed this block is called
if (successBlock)
successBlock();
}];
[[NSOperationQueue mainQueue] addOperations:batches waitUntilFinished:NO];
Try this:
// _group, _queue are iVar variable
dispatch_group_t *_group = dispatch_group_create();
dispatch_queue_t *_queue = dispatch_queue_create("com.company.myqueue2", NULL);
// all files download
for(int i = 0 ; i < numberOfFileDownloads; i++){
dispatch_group_async(_group, _queue, ^{
// here is background thread;
// download file
});
}
// all files are download successfully, this method is called
dispatch_group_notify(_group, _queue, ^{
}
Check out +[AFURLConnectionOperation batchOfRequestOperations:progressBlock:completionBlock:]
Although it's not documented, implementation is self-explanatory. Also it allows you to monitor the progress.
You will need to have an array of HTTP operations prior to using this method (this is if you decided to stick to NSURLConnection-based implementation of AFNetworking).

How to create block with patameters? ios7 [closed]

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
});
}];

How to wait for all asynchronously tasks?

I download asynchronously some object, I store it in array. Next for each object I download some coordinates with geocoding (it is also asynchronously), and update my database for each object with new parameters which is coordinate. My method looks like this:
- (void)downloadObjectsWithTitle:(NSString *)title andHandler:(void(^)(NSMutableDictionary *result))handler {
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:nil
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//I get here array of objects
//now for each object I want to download geocoding localization so i called another asynchronyous method getLocationWithTitle:andHandler;
for(int i = 0; i < resutArray.count; i++) {
[self downloadLocationWithString:[dictionary objectForKey:#"string"] andHandler:^(NSMutableDictionary *result) {
//update database;
}];
}
handler(dictionary);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}
My question is how to downalod coordinates for each object and that fire:
handler(dictionary);
so wait for each coordinates download (for each object) before quit method (fire handler).
Thnaks for all sugestions.
Maintain a count of all the tasks. When it's zero you're done.
Assuming you're using dispatch_async in downloadLocationWithString: on a concurrent queue:
dispatch_barrier_async(queue, ^{
// will only be called after all the blocks submitted to queue have finished.
}];
(If you're using serial queue, simply call handler at the last line of the last block)
Try a global flag. set NO first. In download block, after download complete set flag to yes. You can check that flag.

Resources