AFNetworking and downloading images - ios

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.

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.

Download images to UICollectionView via AFNetworking

I'm new in iOS programming. So I've newbie question. I'm getting started with AFNetworking 2 and that is the task:
I've a request. Its response is the part of the second request. It means that I have to wait untill first request ends. They follow step-by-step. When I get the second response I parse it and save 20 URLs in format http://lalala-xx.jpg. After that I want to load images and put them into UICollectionView, and I want to do it not all in scope but in scheme "downloaded->straight to cell". I save URLs and images in singleton class and get access to them just like
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
cell.imageView.image = [[UserData sharedUser].images objectAtIndex:indexPath.row];
return cell;
}
The chain of methos looks like
- (void)method1
{
NSString *string = [NSString stringWithFormat:firstRequestString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
// getting needed part for second request
[self method2:(NSString *)part1];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// show error
}];
[operation start];
}
Second method:
- (void)method2:(NSString *)part1
{
// lalala making secondRequestString
NSString *string = [NSString stringWithFormat:secondRequestString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSMutableArray *imageURLs = [[NSMutableArray alloc] init];
// getting needed URLs
[self loadAllImages:(NSMutableArray *)imageURLs];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// show error
}];
[operation start];
}
Last:
- (void)loadAllImages:(NSMutableArray *)imageURLs
{
// ???
}
I'm stuck. What should I do next? I have 20 URLs, but how should I download images and direct them to ViewController to update image in cells?
I suppouse AFNetworkig can provide me some operation queue.
And I dont like my code now. I use this chain, but I want an independent method2 returning imgURLs. So it should look:
User presses button -> method1 -> method2 -> stop. Wait untill user presses button -> download image1 -> show image1 -> download image2 -> show image2 -> and so on -> download imageN -> show imageN -> stop. I'll repeat, I need to store images in Array, I'll use it after that.
Thx u read that.
///////////////////////////////////// UPDATE /////////////////////////////////////
I found solution. But it does not satisfy me completely. Images come randomly. How to make them load in order?
- (void)loadAllImages:(NSMutableArray *)imageURLs
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
for (NSURL *url in imageURLs)
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFImageResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
[[UserData sharedUser].images addObject:responseObject];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CollectionViewRealoadData" object:nil];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// show error
}];
[manager.operationQueue addOperation:operation];
}
}
You need to get the data from the URLs and then create a UIImage object from that data.
You can get the data from the URL using the NSURL methods
for(NSString *imgString in imageURLs){
NSURL *url = [NSURL URLWithString:imgString];
NSData *imgData = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:imgData ];
[imageUrls addObject:img];//this mutable array should be initialized early in view controller life cycle (like ViewDidLoad).
}
Once you have your image object you can add it to your array of images that you are using as a datasource for your collection view.
[_collectionView insertItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:[imageUrls count] - 1 inSection:0]]];
//reload your collection view once you add new data

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).

iOS: manage a AFHTTPRequestOperationManager

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.

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