When I use AFNetworking 2, I could get progress with AFHTTPRequestOperation like this:
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:aURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:_timeoutInterval];
AFHTTPRequestOperation *imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
imageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer];
__weak AFHTTPRequestOperation *imageRequestOperationForBlock = imageRequestOperation;
[imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// ...
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// ...
}];
[imageRequestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
if (progress) {
progress((float)totalBytesRead / totalBytesExpectedToRead);
}
}];
I use AFHTTPSessionManager GET:parameters:success:failure: to get data, but I don't know how to get download progress in AFNetworking 3.0.
Link in comments was missleading (NSURLSessionDownloadTask). Sorry about that.
Code below should work though.
Assumption: this code is placed in AFHTTPSessionManager subclass with an NSURLSessionDataTask *testTask ivar declared. It should be easy enough modify as needed.
Vital part of code taken from this answer
- (void)test
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://eoimages.gsfc.nasa.gov/images/imagerecords/78000/78797/nwpassage_tmo_2012199_lrg.jpg"]];
testTask = [self dataTaskWithRequest:request
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error)
{
//...
NSLog(#"error");
}
else
{
//...
UIImage *result = responseObject;
NSLog(#"Success: %#",NSStringFromCGSize(result.size));
}
}];
[self setDataTaskDidReceiveDataBlock:^(NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSData *data)
{
if (dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown)
return;
if (dataTask != testTask)
return;
NSUInteger code = [(NSHTTPURLResponse *)dataTask.response statusCode];
if (!(code> 199 && code < 400))
return;
long long bytesReceived = [dataTask countOfBytesReceived];
long long bytesTotal = [dataTask countOfBytesExpectedToReceive];
NSLog(#"... %lld/%lld",
bytesReceived,
bytesTotal);
}];
[testTask resume];
}
Related
I have this method here:
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
__block BOOL isLoggedIn = YES;
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
isLoggedIn = YES;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
isLoggedIn = NO;
}];
[operation start];
return isLoggedIn;
}
I get no errors, but when I put brake points on success and failure and neither one of them are getting hit. I know the URL is correct and I can see the creds being passed in, I dont know what the issue is ?
UPDATE
I have changed my code to this:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
}
Now I need to add this is my code:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
}
}];
but when I do, I get this error:
Use of undeclared identifier 'self'
I guess this where I am confused because this how I would call this method from another method:
- (void)Login
{
NSString *rawString = [self.idTextField text];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
[self.idTextField setText:[rawString stringByTrimmingCharactersInSet:whitespace]];
BOOL *isAuthenticated = [userName User:self.idTextField.text andPassWordExists:self.passwordTextField.text];
if(isAuthenticated != 0){
[self.idTextField removeFromSuperview];
[self.passwordTextField removeFromSuperview];
[self.loginButton removeFromSuperview];
self.idTextField = nil;
self.passwordTextField = nil;
self.loginButton = nil;
[self CreateMenu];
}else{
[self CustomAlert:#"Sorry Login Failed, User and/or Passsword Incorrect"];
}
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
indicatorView = nil;
[loadingView removeFromSuperview];
loadingView = nil;
}
So inside this:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
}
}];
would I want to either return true or false and my Login method would still work?
UPDATE
I have updated my method to this:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
[self User:^(id responseObject, NSError *error) {
if (responseObject) {
return true;
}
}];
}
Two things wrong here....
I get an error at the end of my fetchDataWithCompletionHandler code:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:237:5: Control may reach end of non-void block
and I get this warning:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:233:11: Instance method '-fetchDataWithCompletionHandler:' not found (return type defaults to 'id')
Am I getting close?
This is an excellent start. I might suggest
changing the closure to simply be BOOL;
eliminate starting the operation until you're done configuring everything (and either call start or add it to a queue, but not both); and
eliminate the call to user at the end.
Thus
- (void)verifyUserid:(NSString *)user password:(NSString *)password completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(TRUE, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(FALSE, error);
}
}];
[operation start];
}
I've also changed the method name to conform to Cocoa conventions (start with verb, method names always begin with lowercase letter), but that's more a stylistic matter than anything else.
Now you can call this method with something like:
[self verifyUserid:user password:password completionHandler:^(BOOL success, NSError* error) {
if (success) {
// userid/password ok; do anything related to successful login here
} else {
// not ok
}
}];
// note, we're not logged in yet (the above completion block is called later),
// so don't assume we're logged in or not here
I am new to AFNetworking and I am having a nightmare of a time with it.
So far I have created this method:
-(void)UserLogin:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
}
I am just super confused on what the next step would be. I have this other method inside another file and I would like to call the UserLogin method and pass in a username and password, would I want to do this like this:
- (void)Login
{
NSString *rawString = [self.idTextField text];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
[self.idTextField setText:[rawString stringByTrimmingCharactersInSet:whitespace]];
[userName User:self.idTextField.text andPassWordExists:self.passwordTextField.text:^(id responseObject, NSError *error) {
if (responseObject) {
//We have a user.
}
}];
}
The idea of your second snippit is correct but it's unclear what the "userName" object is. That should be an instance of the class where the first snippit's method can be located, and the name of the method you created was "UserLogin" (you put "User").
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
I having problem with downloading array of images from server, if i have 100 images on server means i should download all images using "AFImageRequestOperation", while downloading process some images are downloaded successfully but many images are failed to download because of "TimeOut" error from server,i facing Timeout issue with large size image(3.mb),
i using follwing way to downloading images:
NSURL *url = [NSURL URLWithString:kBaseURLString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setAuthorizationHeaderWithUsername:[[NSUserDefaults standardUserDefaults] stringForKey:kUserDefaultKeyUsername]
password:[[NSUserDefaults standardUserDefaults] stringForKey:kUserDefaultKeyPassword]];
for( int i = 0; i < [self.downloadImageList count]; i++ ) {
NSString *filename = [[NSString alloc] initWithString:[self.downloadImageList objectAtIndex:i]];
NSMutableURLRequest *urlRequest = [httpClient multipartFormRequestWithMethod:#"POST"
path:#"/xxx/yyyyyyyyyy/getImage"
parameters:nil
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFormData:[filename dataUsingEncoding:NSUTF8StringEncoding]
name:kFormNameFile];
}];
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
[self updateSyncClientUIDelegateProgress:(totalBytesRead/totalBytesExpectedToRead) andLabel:#"Downloading Images"];
}];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if( [[urlRequest URL] isEqual:[[operation request] URL]] ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = responseObject;
NSLog(#"Downloading image %#",image);
[UIImagePNGRepresentation(image) writeToFile:[syncedImagesPath stringByAppendingPathComponent:filename] atomically:YES];
});
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if( [[operation response] statusCode] == 404 ) {
return;
}
NSLog(#"failure BLOCK %#",error);
NSLog(#"failure error code %ld",(long)[error code]);
if( [error code] != NSURLErrorCannotDecodeContentData ) {
[self cancelSyncFromFailure];
}
}];
Please help me to fix this timeout issue while downloading large iamges
NOW HERE IS THE SOLUTION I AM USING AND WORKING FINE.FIRSTLY IT WAS ALSO SENDING ME THE "REQUEST TIMEOUT" MESSAGE BUT NOT NOW.TRY THIS
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:photourl] cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10000];
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
...... YOUR CODE
}];
You set TimeOut Request Time is more a below code.
[urlRequest setTimeoutInterval:50];
I have an url request method, because each page will be used, I want to make it public.
(Poor standard of English, forgive me)
here is my class method:
- (void)httpRequest :(NSString *)url :(NSMutableArray *)array;
{
NSURL *myUrl = [NSURL URLWithString:url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:myUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *arr;
arr = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:NULL];
[array addObjectsFromArray:arr];
???????
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"请求失败: %#", error);
??????
}];
[operation start];
}
I know how to pass simple parameters,like array and url.But when I use this method,code in the question mark will be different everytime.What can I do to make it can be passed,like parameters.
Like: [myApple httpRequest :myurl :myarray : (susucess .....) : (failure......)];
the question mark can be filled in: mytableView reloadData or nslog...
I'm not sure if i explained my question clearly,i don't know if block can solve this,thanks,waiting for your help.
Firstly, note that all method parameters in Objective-C should be named. So instead of - (void)httpRequest :(NSString *)url :(NSMutableArray *)array;, you should have something like:
- (void)performHttpRequestWithURL:(NSString *)url resultsArray:(NSMutableArray *)array;
To answer your question, you could pass your own completion block into your method. You can then call this block once AFNetworking has completed its HTTP request.
- (void)performHTTPRequestWithURL:(NSString *)urlString completion:(void (^)(NSArray *results, NSError *error))completion
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *results = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:nil];
if (completion)
completion(results, nil);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"请求失败: %#", error);
if (completion)
completion(nil, error);
}];
[operation start];
}
You could then call your method and pass in a block:
[self performHTTPRequestWithURL:#"http://www.example.com" completion:^(NSArray *results, NSError *error) {
if (error)
{
// handle error
}
else
{
// reload tableview or similar
}
}];
Take a look at Apple's Blocks Programming Guide for more information on blocks.