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..!
Related
What I am trying to do is, on a click of a button I am fetching data using AFNetworking pod. What i want is after fetching data I want to display NSLog(#"/n /nAfter fetching"); but it is coming before the json data.
Here is the code I have written in IBAction of Button.
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
//Load the json on another thread
NSURL *url = [NSURL URLWithString:finalURL];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:configuration];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response,id responseObject, NSError *error) {
if (error) {
NSLog(#"\n Error --> %#",error);
}
else{
jsonDictionary = (NSDictionary*) responseObject;
NSLog(#"/n Data :: %#",jsonDictionary);
}
}];
[dataTask resume];
});
NSLog(#"/n /nAfter fetching");
Please provide a nice way of doing it and do correct me If I have gone wrong with it. Thank you.
As AFURLSessionManager makes the Asynchronous operation. YOu will get the data in completion
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
//Load the json on another thread
[self startSpinner];
NSURL *url = [NSURL URLWithString:finalURL];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:configuration];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response,id responseObject, NSError *error) {
[self stopSpinner];
if (error) {
NSLog(#"\n Error --> %#",error);
}
else{
NSLog(#"/n /nAfter fetching"); //this is where you receive data
jsonDictionary = (NSDictionary*) responseObject;
NSLog(#"/n Data :: %#",jsonDictionary);
}
}];
[dataTask resume];
});
if you want to make synchronous operation using URLSession below method will help you
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(__autoreleasing NSURLResponse **)responsePtr
error:(__autoreleasing NSError **)errorPtr {
dispatch_semaphore_t sem;
__block NSData * result;
result = nil;
sem = dispatch_semaphore_create(0);
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (errorPtr != NULL) {
*errorPtr = error;
}
if (responsePtr != NULL) {
*responsePtr = response;
}
if (error == nil) {
result = data;
}
dispatch_semaphore_signal(sem);
}] resume];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return result;
}
hello friends i m trying to upload audio file which is record by me its self but i m unable to upload that audio file into server. can any one suggest me proper way to upload that below have the code written by me.
player = [[AVAudioPlayer alloc] initWithContentsOfURL:recorder.url error:nil];
NSURL *audioURL = recorder.url;
NSString *str = [NSString stringWithFormat:#"%#",audioURL];
[mainAudioAddressArray addObject:str];
NSLog(#"%#",str);
NSLog(#"%#",mainAudioAddressArray);
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://192.168.1.4:8080/D:/Test/"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURL *filePath = [NSURL fileURLWithPath:#"str"];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error)
{
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"Success: %# %#", response, responseObject);
}
}];
[uploadTask resume];
I am using the following code to download a file from the web. How do i structure the code (using blocks) such that, if it fails, it will be retried with a max of retrycount. Here is the code that i am using to download the file.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *myURL = [NSURL URLWithString:#"urlstring"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL];
[request setValue:token forHTTPHeaderField:#"Authorization"];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *directoryURL = [NSURL fileURLWithPath:NSTemporaryDirectory()];
return [directoryURL URLByAppendingPathComponent:fileName];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
//check here if error then retry using block ?
if (!error)
{
//do something with the file
}
else
{
//retry download ??
}
}
}];
[downloadTask resume];
You could create a retryCount instance variable and set it to however many times you want to retry the network call. Then you could put all your networking code in a method with a custom completion handler. Your completion handler could be a block that takes a BOOL as a parameter and returns void. Then, in your networking code, create a local BOOL variable and set it to YES or NO based on whether the network call succeeded or failed. You'll run the completion handler argument within your networking code. Then the block (completion handler) that you send as a parameter to the method can "see" whether the local variable in your method was set to YES or NO. If it was YES, the network call succeeded, so do nothing. If it was NO, the network call failed, so check retryCount--if it's > 0, call the networking method again and decrement retryCount.
UPDATE
This should give you a rough idea...
typedef void (^CustomCompletionHandler)(BOOL); // Create your CustomCompletionHandler type
- (void)getDataAndCallCompletionHandler:(CustomCompletionHandler *)completionHandler {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *myURL = [NSURL URLWithString:#"urlstring"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL];
[request setValue:token forHTTPHeaderField:#"Authorization"];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
BOOL success = NO; // Create local variable to keep track of whether network call succeeded or failed
NSURL *directoryURL = [NSURL fileURLWithPath:NSTemporaryDirectory()];
return [directoryURL URLByAppendingPathComponent:fileName];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
//check here if error then retry using block ?
if (!error)
{
success = YES;
//do something with the file
}
else
{
success = NO;
//retry download ??
}
}
// Get main queue and call completionHandler and pass success:
// completionHandler(success)
}];
[downloadTask resume];
}
You would call this method by doing something like...
[self getDataAndCallCompletionHandler:^(BOOL success) {
// If success == true {
// Do whatever you need to
// } else {
// Check retryCount
// If you need to, call getDataAndCallCompletionHandler: again
// }
}];
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];
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];