AFNetworking Memory - afnetworking

Using AFNetworking to download files from a server. Here's the code:
self.networkQueue = [[[NSOperationQueue alloc] init] autorelease];
[networkQueue setMaxConcurrentOperationCount:3];
for(NSDictionary* fileDictionary in self.syncArray) {
#autoreleasepool {
if([[fileDictionary allKeys] containsObject:#"downloadZipURL"]) {
NSString* downloadPath = [fileDictionary objectForKey:#"downloadZipURL"];
downloadPath = [downloadPath stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURLRequest *requestURL = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadPath]];
NSString* localDestPath = [NSString stringWithFormat:#"%#/%#", [FileUtil userDocumentsDirectory], [downloadPath lastPathComponent]];
NSString* localTempPath = [NSString stringWithFormat:#"%#.tmp", localDestPath];
[(NSMutableDictionary*)fileDictionary setObject:localDestPath forKey:#"downloadDestination"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:localDestPath append:NO];
operation.userInfo = fileDictionary;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
// [operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// NSLog(#"Sent %lld of %lld bytes, %#", totalBytesWritten, totalBytesExpectedToWrite, localDestPath);
// float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
// [(NSMutableDictionary*)operation.userInfo setObject:[NSString stringWithFormat:#"Downloading %.0f%%", progress*100] forKey:#"downloadStatus"];
// [(NSMutableDictionary*)operation.userInfo setObject:[NSNumber numberWithFloat:progress] forKey:#"downloadProgress"];
// [syncViewController onPermitUpdated];
// }];
[networkQueue addOperation:operation];
}
}
}
My problem is that once this code is run, memory slowly gets eaten up and never given back. Now, these can be large files, which is why I used the outputStream.
Any suggestions would be appreciated.

Off the top of my head - I see that you're not using ARC.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL]
Are you releasing this operation somewhere?
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
Here, you're using the networkQueue in the completionBlock and the block retains the networkQueue, you then add the operation to the networkQueue, which retains the operation, which leads to neither of them deallocating. Try making a weak variable of the networkQueue and use that in order to break the cycle.
If these don't work - run instruments and make a note of what objects remain in memory and when their reference count is changed.

Related

UIlabel text not getting update when download files in progress

I am trying to update the `UILabel` i.e downloaded data and remeaning data to be downloaded estimated time and total size of the downloading files via `NSnotificationCenter`, but not being updated `UILabel` text Please help me on this.
Also tried putting the `NSnotificationCenter` block in the main thread but no result found.
I have tried like this:
- (AFHTTPRequestOperation )downloadMediaOperation:(ILSCDowloadMedia )media success:(void (^)(ILSCDowloadMedia *media))success {
if (media.mediaUrl.length == 0) nil;
__block NSString *mediaKey = [[NSUserDefaults standardUserDefaults] objectForKey:media.mediaUrl];
NSURL *url = [NSURL URLWithString:media.mediaUrl];
if (mediaKey.length == 0) {
mediaKey = [NSString stringWithFormat:#"%#.%#", [ILSCUtility createUUID], [[[url path] lastPathComponent] pathExtension]];
}
NSFileManager *fileManager= [NSFileManager defaultManager];
NSString *mediaFilePath = NIPathForDocumentsResource(mediaKey);
media.mediaFilePath = mediaFilePath; if (![fileManager fileExistsAtPath:mediaFilePath]) {
__weak ILSCSyncManager *weakSelf = self;
NSURLRequest *request = [self.HTTPClient requestWithMethod:#"GET" path:[url path] parameters:nil];
AFHTTPRequestOperation *downLoadOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
downLoadOperation.downloadSpeedMeasure.active = YES; [downLoadOperation setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{
// Clean up anything that needs to be handled if the request times out
// It may be useful to initially check whether the operation finished or was cancelled
}];
downLoadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:mediaFilePath append:NO];
[downLoadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[[NSUserDefaults standardUserDefaults] setObject:mediaKey forKey:media.mediaUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
if (success) {
success(media);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NIDERROR(#"y error %#", [error localizedDescription]);
__strong ILSCSyncManager *strongSelf = weakSelf;
strongSelf.numberOfDownloadErrors++;
}];
[downLoadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSLog(#"vvv Byted total expected to read %f",totalImagesBytesExpectedToRead);
totalImagesBytesRead += bytesRead;
humanReadableSpeed = downLoadOperation.downloadSpeedMeasure.humanReadableSpeed;
humanReadableRemaingTime = [downLoadOperation.downloadSpeedMeasure humanReadableRemainingTimeOfTotalSize:totalImagesBytesExpectedToRead numberOfCompletedBytes:totalImagesBytesRead];
NSLog(#"Speed Human %#",humanReadableSpeed);
NSLog(#"Time is human read %#",humanReadableRemaingTime);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateProgressBar" object:[NSString stringWithFormat:#"%#-%f-%f-%#", humanReadableSpeed,totalImagesBytesRead,totalImagesBytesExpectedToRead,humanReadableRemaingTime]];
});
}];
return downLoadOperation;
} else {
if (success) {
success(media);
}
}
return nil;
}
Please help me on this.
This is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:#"UpdateProgressBar" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSString *str =[note object]; NSArray *arrayTotalOperationsIn = [str componentsSeparatedByString:#"-"];
NSLog(#"%#",arrayTotalOperationsIn); self.lblSpeedMeasure.text =[NSString stringWithFormat:#"Internet Speed - %#" ,[arrayTotalOperationsIn objectAtIndex:0]];
float bytesRead = [[arrayTotalOperationsIn objectAtIndex:1] floatValue];
float bytesExpectedToRead = [[arrayTotalOperationsIn objectAtIndex:2] floatValue];
NSString *timeExpectedToRead = [arrayTotalOperationsIn objectAtIndex:3];
self.progressCountTextLabel.text=[NSString stringWithFormat:#"%.2f MB/%.2f MB - %# Left",bytesRead/1000000,bytesExpectedToRead/1000000,timeExpectedToRead];
}];
The above is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .
Try calling the setNeedsDisplay method on your UILabel after setting the text
[self.progressCountTextLabel setNeedsDisplay];

Error when uploading photo with AFNetworking

I am uploading a photo using AFNetworking and I am getting the infamous "request body stream exhausted" error.
This is my code:
(_manager is a AFHTTPRequestOperationManager)
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
AFHTTPRequestOperation *operation = [_manager POST:address parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:#"file" fileName:#"image.jpg" mimeType:#"image/jpeg"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Written: %lld", totalBytesWritten);
}];
I get the error on both my iPhone 5S and my iPhone 4, both using wifi and 4G/3G. The issue is consistent and occurs every time, just when the upload is suppose to finish. The weird thing is that it use to work on these phones as well, but some days ago I suddenly started getting the error. Also, my colleague has no problems on his iPhone 5, both on wifi and 4G, running the same code. All phones run iOS 7.
I know that some people are getting this error when on 3G, and the solution in that situation is to use the throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay method. However, this has had no affect in my case, and I get the error both on wifi and mobile.
I am able to perform the upload using curl from my computer which is on the same network.
I finally found a solution in this answer:
https://stackoverflow.com/a/21304062/569507
However, in stead of subclassing the AFHTTPRequestOperation as suggested, I simply made a category on it (or actually on its parent class AFURLConnectionOperation which implements the NSURLConnectionDataDelegate protocol) that contains the connection:needNewBodyStream method. This is all that is needed. The rest of my code remains unaltered.
AFURLConnectionOperation+Recover.h
#import "AFURLConnectionOperation.h"
#interface AFURLConnectionOperation (Recover)
- (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request;
#end
AFURLConnectionOperation+Recover.m
#import "AFURLConnectionOperation+Recover.h"
#implementation AFURLConnectionOperation (Recover)
- (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request
{
if ([request.HTTPBodyStream conformsToProtocol:#protocol(NSCopying)]) {
return [request.HTTPBodyStream copy];
}
return nil;
}
#end
NSData *imageData = UIImageJPEGRepresentation(img,1.0);
long long testBytes = 0;
for (int i=0; i<[arrImages count];i++)
{
UIImage *img =[arrImages objectAtIndex:i];
img=[AppManager scaleDownImage:img];
NSData *imageData1 = UIImageJPEGRepresentation(img,1.0);
testBytes +=[imageData1 length]+130;
}
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
NSMutableURLRequest *request =
[serializer multipartFormRequestWithMethod:#"POST" URLString:[NSString stringWithFormat:#"%#%#",kBaseURL,kChallengeURL] parameters:aDic constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData
name:kProfileImage
fileName:#"myimage.jPEG"
mimeType:#"image/jpeg"];
}
error:nil];
//
// [serializer multipartFormRequestWithMethod:#"POST" URLString:[NSString stringWithFormat:#"%#%#",kBaseURL,kChallengeURL]
// parameters:aDic
// constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// [formData appendPartWithFileData:imageData
// name:#"attachment"
// fileName:#"myimage.jpg"
// mimeType:#"image/jpeg"];
// }];
// 3. Create and use `AFHTTPRequestOperationManager` to create an `AFHTTPRequestOperation` from the `NSMutableURLRequest` that we just created.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *operation =
[manager HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if ([[responseObject objectForKey:kStatus] intValue]==1)
{
NSArray *arrImages = [app.dicCreateChallangeDetail objectForKey:kProfileImage];
if ([arrImages count]==1)
{
[AppManager sharedManager].enCheckCreateService = eCreateWebserviceInStopped;
[[NSNotificationCenter defaultCenter]postNotificationName:kNotificationEnableTabBarButton object:nil userInfo:nil];
self.view.userInteractionEnabled = YES;
self.navigationItem.leftBarButtonItem.enabled = YES;
[self performSelector:#selector(goToHomeScreen) withObject:nil afterDelay:0.0];
}
else if ([arrImages count]>1)
{
[self uploadRestImages:[responseObject objectForKey:kChallengeId]];
}
}
else
{
[AppManager sharedManager].enCheckCreateService = eCreateWebserviceInStopped;
self.view.userInteractionEnabled = YES;
self.navigationItem.leftBarButtonItem.enabled = YES;
[Utils showAlertView:kAlertTitle message:[responseObject objectForKey:kMessage] delegate:nil cancelButtonTitle:kAlertBtnOK otherButtonTitles:nil];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error){
//NSLog(#"Failure %#", error.description);
[AppManager sharedManager].enCheckCreateService = eCreateWebserviceInStopped;
self.view.userInteractionEnabled = YES;
self.navigationItem.leftBarButtonItem.enabled = YES;
[Utils showAlertView:kAlertTitle message:#"Failed. Try again" delegate:nil cancelButtonTitle:kAlertBtnOK otherButtonTitles:nil];
[[NSNotificationCenter defaultCenter]postNotificationName:kNotificationEnableTabBarButton object:nil userInfo:nil];
}];
// 4. Set the progress block of the operation.
[operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten,
long long totalBytesWritten,
long long totalBytesExpectedToWrite) {
NSLog(#"Wrote %lld/%lld", totalBytesWritten, totalBytesExpectedToWrite);
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:#"%lld",totalBytesWritten],#"written",[NSString stringWithFormat:#"%lld",totalBytesExpectedToWrite],#"Totlal",nil];
[[NSNotificationCenter defaultCenter]postNotificationName:kNotificationRefreshHomePage object:dict userInfo:nil];
}];
// 5. Begin!
[operation start];

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

Restkit RKObjectManager requests timeout when downloading a lot of files

First off: I'm quite new to RestKit so perhaps this is an easy question to solve.
I am trying to download a lot of files. Currently I use the getObjectsAtPath:parameters:success:failure: method of RKObjectManager to fetch and map my objects towards restkit.
However, It seems as it starts some downloads prematurely and then timesout when they are in the queue.
The code I am using:
- (void)removeResponseAndRequestDescriptors
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager.requestDescriptors enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[objectManager removeRequestDescriptor:obj];
}];
[objectManager.responseDescriptors enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[objectManager removeResponseDescriptor:obj];
}];
}
.
- (void)downloadAudioFileForAudio:(IBAudio *)audio
inBook:(IBBook *)book
downloadStatus:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))downloadStatus
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
NSString *sessionID = (book.parent ? book.parent.user.session.sessionID : book.user.session.sessionID);
[objectManager.HTTPClient setDefaultHeader:#"Accept" value:#"application/octet-stream"];
[objectManager.HTTPClient setDefaultHeader:#"Session-Id" value:sessionID];
[objectManager.HTTPClient getPath:[IBProperties downloadAudioEndPointWithIsbn:book.isbn andAnchor:audio.anchor] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *folderPath = [self folderPathForBook:book];
NSString *audioPath = [folderPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mp3", audio.anchor]];
NSData *audioData = [NSData dataWithData:responseObject];
NSError *fileSystemSaveError;
[self saveFile:audioData toFilePath:audioPath error:&fileSystemSaveError];
if (fileSystemSaveError) {
failure(fileSystemSaveError);
return;
}
// Saving the context asap in case the app dies before it can autosave.
NSError *coreDataSaveerror;
[[[[RKObjectManager sharedManager] managedObjectStore] mainQueueManagedObjectContext] save:&coreDataSaveerror];
if (coreDataSaveerror) {
failure(coreDataSaveerror);
return;
}
[audio setFilePath:audioPath];
success();
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
[objectManager.HTTPClient.operationQueue.operations.lastObject setDownloadProgressBlock:downloadStatus];
}
.
- (void)downloadAudioFiles
{
for (IBAudio *audio in self.book.bookData.audios) {
self.numberOfDownloads++;
[self.downloadPercentagesFiles addObject:[[IBDownloadStatusOfAFile alloc] init]];
NSInteger arrayIndex = [self.downloadPercentagesFiles count] - 1;
[[IBDataBackendFetcher sharedBackendFetcher] downloadAudioFileForAudio:audio inBook:self.book downloadStatus:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
IBDownloadStatusOfAFile *statusOfFile = [self.downloadPercentagesFiles objectAtIndex:arrayIndex];
statusOfFile.bytesRead = bytesRead;
statusOfFile.totalBytesRead = totalBytesRead;
statusOfFile.totalBytesExpectedToRead = totalBytesExpectedToRead;
[self updateDownloadProgress];
} success:^{
[self downloadDidComplete];
} failure:^(NSError *error) {
[self.dataProviderDelegate downloadDidFailWithError:error forBookDownloader:self];
}];
}
}
It seems as it starts all downloads at once, but the actuall download is not started. So the last downloads gets a timeout.
Is there a better way for do this to solve this problem?
All the downloads will run simultaneously, because you're making all the getPath: calls right in a row (they are asynchronous calls). Since each download takes a while to finish, this causes the timeout on the later calls.
If you want each download to occur only after the previous one completes, I would make a method called getNextAudioFile: and an iterator class property. Then, in both the success and failure blocks of getPath:, increment your iterator and call getNextAudioFile:.
Example code:
- (void)downloadAudioFiles
{
// No for loop
self.iterator = 0;
// your call to DownloadAudioFileForAudio: ... for the first audio goes here
}
- (void)downloadAudioFileForAudio:(IBAudio *)audio
inBook:(IBBook *)book
downloadStatus:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))downloadStatus
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure
{
// your code ...
[objectManager.HTTPClient getPath:[IBProperties downloadAudioEndPointWithIsbn:book.isbn andAnchor:audio.anchor] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject){
// your code...
// increment and get the next file
self.iterator++;
[self getNextAudioFile];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
// increment and get the next file
self.iterator++;
[self getNextAudioFile];
}];
}
- (void)getNextAudioFile
{
if(self.iterator < [self.book.bookData.audios count]){
// make your downloadAudioFileForAudio: call for the next audio
}
}
That's the idea at least! Hope it helped.
Set the maximum number of concurrent operation on the queue to some reasonable value like 5.
[objectManager.HTTPClient.operationQueue setMaxConcurrentOperationCount:5];
(before you start any requests)

Resources