Chain request using NSURLSessionDataTask - ios

I am to execute a chain of request using NSURLSessionDataTask. When first request finished, I need to use the responseData from the fist request to execute another multiple request. and In the end, I get the NSArray and give to to the table view. How to do that? As you can see below, it's not working.
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:config];
NSString *tvdbId = [[NSUserDefaults standardUserDefaults] objectForKey:#"tvdbId"];
NSURL *urlString = [NSURL URLWithString:[NSString stringWithFormat:#"http://api.trakt.tv/show/seasons.json/%#/%#", kApiKey, tvdbId]];
__weak EpisodeViewController *weakSelf = self;
NSURLSessionDataTask *task = [manager dataTaskWithRequest:[NSURLRequest requestWithURL:urlString] completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (!error) {
NSArray *seasons = (NSArray *)responseObject;
__block NSMutableArray *seasonArray = [NSMutableArray new];
[seasons enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *seasonNumber = obj[#"season"];
NSURL *urlString = [NSURL URLWithString:[NSString stringWithFormat:#"http://api.trakt.tv/show/season.json/%#/%#/%#", kApiKey, tvdbId, seasonNumber]];
NSURLSessionDataTask *eposideTask = [manager dataTaskWithRequest:[NSURLRequest requestWithURL:urlString] completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSArray *eposides = (NSArray *)responseObject;
NSDictionary *dict = #{#"season": seasonNumber, #"eposodes": eposides};
[seasonArray addObject:dict];
}];
[eposideTask resume];
}];
weakSelf.eposides = [NSArray arrayWithArray:seasonArray];
NSLog(#"%#", weakSelf.eposides);
}
}];
[task resume];

You can Use AFNetworking if you are downloading data
Add your operations (in your case NSURLSessionDataTask) into a NSOperationQueue and
set maximumconcurrentoperationCount to 1
from Completion call back of each operation get the downloaded data (result of operation)
Sample Code
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"temp.zip"]];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:TempUrl]];
operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setProgressiveDownloadProgressBlock:^(AFDownloadRequestOperation *operation, NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
/// Check Download Progress
}
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//// Success code goes here
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[[self downloadQueue] addOperation:operation];

Related

Download one file at a time using afnetworking

I have an array which contains different URLs. I want to download one file with a progress bar, than start the next one and so on.
Here is my code that I have so far;
-(void) func:(NSArray *) arry
{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.timeoutIntervalForRequest = 900;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSMutableArray * downloadedUrl = [[NSMutableArray alloc] init];
for (NSString * str in arry) {
NSURL *URL = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL (NSURL targetPath, NSURLResponse response) {
NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
return [tmpDirURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse response, NSURL filePath, NSError *error) {
if(error)
{
NSLog(#"File Not Dowloaded %#",error);
}
}];
[downloadTask resume];
}
}
How would you download one file at a time with a progress bar and then remove the url from array?
Declare one global NSMutableArray of file and used that in the function like below.
-(void) downloadFile {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.timeoutIntervalForRequest = 900;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:[self.fileArr firstObject]]; //Here self.fileArr is your global mutable array
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL (NSURL targetPath, NSURLResponse response) {
NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
return [tmpDirURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse response, NSURL filePath, NSError *error) {
if(error)
{
NSLog(#"File Not Dowloaded %#",error);
[self downloadFile];
}
else {
[self.fileArr removeObjectAtIndex:0];
if (self.fileArr.count > 0) {
[self downloadFile];
}
}
}];
[downloadTask resume];
}
Now call this function but before that initialize self.fileArr and after that call downloadFile method.
Hope this will help you.
Give limit the queue to one Operation at a time,
For that, Try to adding dependencies between each operation before you queue them.
If you add dependency between two operations say operation1 and operation2 before adding to queue then the operation2 will not start until operation1 is finished or cancelled.
Do it like:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
// Make operation2 depend on operation1
[operation2 addDependency:operation1];
[operationQueue addOperations:#[operation1, operation2, operation3] waitUntilFinished:NO];
UPDATE
// Create a http operation
NSURL *url = [NSURL URLWithString:#"http://yoururl"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
// Add the operation to a queue
// It will start once added
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
hope it help you..!

Upload image with post -ios

i use this method to save user informations in data base. Can someone help me to upload image too in the same function ?
- (void)Inscription:(NSArray *)value completion:(void (^)( NSString * retour))block{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSArray *Param_header = #[#"username", #"password", #"email",#"first_name", #"last_name"];
// NSArray *Param_value = #[#"ali", #"aliiiiiiii", #"ali.ali#gmail.com",#"ali",#"zzzzz"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http:// /Messenger/services/messenger_register"]]];
NSString *aa=[self buildParameterWithPostType:#"User" andParameterHeaders:Param_header ansParameterValues:value];
[request setHTTPBody:[aa dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest: request
queue: queue
completionHandler: ^(NSURLResponse *response, NSData *data, NSError *error) {
if (error || !data) {
// Handle the error
NSLog(#"Server Error : %#", error);
} else {
NSError *error = Nil;
id jsonObjects = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
block([NSString stringWithFormat:#"%#", [jsonObjects objectForKey:#"message"]]);
}
}
];
}
You can use third-party frameworks like AFN to upload image。
Here is sample code:
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"username"] = #"Jack";
[mgr POST:#"http:// /Messenger/services/messenger_register" parameters:params
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSString *file = [[NSBundle mainBundle] pathForResource:#"image.png" ofType:nil];
NSData *data = [NSData dataWithContentsOfFile:file];
[formData appendPartWithFileData:data name:#"file" fileName:#"image.png" mimeType:#"mage/png"];
}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"upload success!----%#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"upload failed!----%#", error);
}];

calculating total progress of downloading multiple file with AFNetworking

I want to download multiple file and then display a total progress to the user.
but the problem is here I dont know how should I calculate total progress .
here is what I do:
first I get to totalBytes Expected to receive from all of the files:
for (NSURL candidateUrl in UrlsList)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
//setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
[request setHTTPMethod:#"HEAD"];
getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
totalImagesBytesExpectedToRead += [operation.response expectedContentLength];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operationQueue addOperation:getTotalImagesBytesOperation];
}
after estimating total files size:
//downloading images
for (NSURL *imageUrl in imagesURLList) {
NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
AFImageRequestOperation *downloadImageOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request
imageProcessingBlock:nil
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"success")
}
failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"%#", [error localizedDescription]);
}];
[operationQueue addOperation:downloadImageOperation];
[downloadImageOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
**HOW TO CALCULATE TOTAL PROGRESS**
}
I don't know how to calculate total size!!
the values we have:
totalBytesOfAllTheFiles, totalBytesRead and totalBytesExpectedToRead for the current file which above method gives you, indexOfCurrentFile and countOfFiles.
be cautious setDownloadProgressBlock called hundreds a time.
does anyone have any idea? (sorry for bad formatting of code!)
Here is how I achieved that:
first create an NSOperationQueue:
// Add the operation to a queue
// It will start once added
//calculating images byte size
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
foreach (NSURL *candidateURL in urlList )
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
//setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
[request setHTTPMethod:#"HEAD"];
AFHTTPRequestOperation *getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
totalImagesBytesExpectedToRead += [operation.response expectedContentLength];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operationQueue addOperation:getTotalImagesBytesOperation];
}
//downloading images which should be downloaded
for (NSURL *imageUrl in imagesShouldBeDownlaoded) {
NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
AFHTTPRequestOperation *downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//we must provide file path with file name and its extension
NSString *fileName = [self getImageName:[imageUrl absoluteString]];
downloadFileOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:[[ImageManager applicationDocumentsDirectory] stringByAppendingPathComponent:fileName] append:NO];
[downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
{
NSLog(#"file saved");
}failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
completionBlock(NO, error);
NSLog(#"%#", [error localizedDescription]);
}];
[operationQueue addOperation:downloadFileOperation];
[downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
totalBytesDownloaded += bytesRead;
//total progress from 0.0 to 1.0
progress = ((float)totalBytesDownloaded/(float)totalImagesBytesExpectedToRead);
if(progress == 1)
{
completionBlock(YES, nil);
}
progressCallback(progress);
}];
operationQueue works as a FIFO list, first calculate images byte size, then starts to download all images
AFNetworking3 use dispatch_group and NSProgress to achieved this:
#import <Foundation/Foundation.h>
#import <AFNetworking.h>
#interface Server : NSObject
#property (nonatomic, strong) AFHTTPSessionManager *manager;
#property (nonatomic, strong) NSProgress *progress;
- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls;
#end
#import "Server.h"
#implementation Server
- (instancetype)init
{
self = [super init];
if (self) {
_manager = [AFHTTPSessionManager manager];
_manager.requestSerializer = [AFJSONRequestSerializer serializer];
_manager.responseSerializer = [AFJSONResponseSerializer serializer];
_progress = [NSProgress progressWithTotalUnitCount:0];
[_progress addObserver:self forKeyPath:#"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(#"fractionCompleted:%lf",self.progress.fractionCompleted);
}
- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls {
//progress
dispatch_group_t group = dispatch_group_create();
_progress.totalUnitCount = urls.count;
[urls enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
NSURLRequest *request = [NSURLRequest requestWithURL:obj];
NSURLSessionDownloadTask *task = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil] URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(#"one task completion");
dispatch_group_leave(group);
}];
NSProgress *child = [self.manager downloadProgressForTask:task];
[self.progress addChild:child withPendingUnitCount:1];
[task resume];
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"%lf", self.progress.fractionCompleted);
});
}
#end

AFHTTPREquestOperation doesn't work on iPad

I'm trying to compile application on my iPad. I'm using AFNetworking to get list of files on my FTP. Application works on simulator, but when i start it on iPad i get (null) content of file with list. Here's code:
- (void) getListOfFiles {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"ftp://anonymous#ftphost/"]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"list"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response) {
NSLog(#"Success %#",operation.response);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
[operation start];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog (#"%#",content);
}
So variable content = (null) only on iPad, and on Simulator everything is fine. Please help, i have lost any hope )
the AFHTTP*Operations are all asynchronous by default.
this is proper, since synchronous(blocking) calls block the main thread
you are starting the operation and take file content directly afterwards. This can't work reliably because the start call is ASYNC and only STARTS the op but doesnt wait for it
either wait for it ... which is BAD as it blocks the thread:
[operation start];
[operation waitUntilDone];
or MUCH BETTER modify getFiles to work asynchronously:
- (void) getListOfFiles {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"URL"]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"list"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response) {
NSLog(#"Success %#",operation.response);
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog (#"%#",content);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
[operation start];
}
Ok, problem solved. I can't write to bundle directory, so i nee to use next code instead:
NSArray *paths = NSSearchPathForDirectoiesDomain(NSDocumentDirectory,NSCachesDirectory,YES);
NSString *path = [[paths objectAtIndex:0] stingByAppendingPathComponent:#"list"];

How to download a file and save it to the documents directory with AFNetworking?

I am using the AFNetworking library. I can't figure out how to download a file and save it to the documents directory.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"..."]];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"filename"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
I'm gonna bounce off #mattt's answer and post a version for AFNetworking 2.0 using AFHTTPRequestOperationManager.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"filename"];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *op = [manager GET:#"http://example.com/file/to/download"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"successful download to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
I'm talking about AFNetworking 2.0
[AFHTTPRequestOperationManager manager] creates manager object with default AFJSONResponseSerializer, and it performs content types restriction. Take a look at this
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
So we need to create a none response serializer and use AFHTTPRequestOperationManager as normal.
Here is the AFNoneResponseSerializer
#interface AFNoneResponseSerializer : AFHTTPResponseSerializer
+ (instancetype)serializer;
#end
#implementation AFNoneResponseSerializer
#pragma mark - Initialization
+ (instancetype)serializer
{
return [[self alloc] init];
}
- (instancetype)init
{
self = [super init];
return self;
}
#pragma mark - AFURLResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
return data;
}
#end
Usage
self.manager = [AFHTTPRequestOperationManager manager];
self.manager.responseSerializer = [AFNoneResponseSerializer serializer];
[self.manager GET:#"https://sites.google.com/site/iphonesdktutorials/xml/Books.xml"
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if (success) {
success(responseObject);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (failure) {
failure(error);
}
}];
so that we can get the whole file without any serialization
Documentation page has example with section 'Creating a Download Task':
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(#"File downloaded to: %#", filePath);
}];
[downloadTask resume];
NB! Code work with iOS 7+ (tested with AFNetworking 2.5.1)
From AFNetworking docs.
Save to loaded file to your documents.
AFNetworking 3.0
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(#"File downloaded to: %#", filePath);
}];
[downloadTask resume];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFCompoundResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"application/octet-stream"];
AFHTTPRequestOperation *operation = [manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (responseObject) {
// your code here
} else {
// your code here
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
// manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject: #"application/octet-stream"]; can vary depending on what you expect
Yes, it is better to use AFNetworking 2.0 way with AFHTTPRequestOperationManager. With old way my file did download but for some reason didn't update in file system.
Appending to swilliam's answer, to show download progress, in AFNetworking 2.0 you do similarly - just set download progress block after setting output stream.
__weak SettingsTableViewController *weakSelf = self;
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:newFilePath append:NO];
[operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) {
float progress = totalBytesWritten / (float)totalBytesExpectedToRead;
NSString *progressMessage = [NSString stringWithFormat:#"%# \n %.2f %% \n %# / %#", #"Downloading ...", progress * 100, [weakSelf fileSizeStringWithSize:totalBytesWritten], [weakSelf fileSizeStringWithSize:totalBytesExpectedToRead]];
[SVProgressHUD showProgress:progress status:progressMessage];
}];
This is my method to create bytes string:
- (NSString *)fileSizeStringWithSize:(long long)size
{
NSString *sizeString;
CGFloat f;
if (size < 1024) {
sizeString = [NSString stringWithFormat:#"%d %#", (int)size, #"bytes"];
}
else if ((size >= 1024)&&(size < (1024*1024))) {
f = size / 1024.0f;
sizeString = [NSString stringWithFormat:#"%.0f %#", f, #"Kb"];
}
else if (size >= (1024*1024)) {
f = size / (1024.0f*1024.0f);
sizeString = [NSString stringWithFormat:#"%.0f %#", f, #"Mb"];
}
return sizeString;
}
In addition to the previous answers, with AFNetworking 2.5.0 and iOS7/8 I have found that that the extra step of opening the output stream is also needed to prevent the app from hanging (and eventually crashing from lack of memory).
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:dest
append:NO];
[operation.outputStream open];
[operation start];

Resources