简体   繁体   中英

AFNetworking 2.0 + UIProgressView how to correctly display progress for an upload session

I'm using an AFHTTPSessionManager subclass to download and upload data through webservices. Everything seems to work fine except for the upoload progress view, I'm using UIProgressView+AFNetworking category.
Code is set like that:

 NSURLSessionTask * task = [[AFHTTPSharedClient sharedHTTPClient] addMediaWithURI:self.filePath success:^(NSURLSessionDataTask *task, BOOL added) {
        if (added) {
            NSLog(@"Operation succ");
        }
        weakSelf.progressContainerView.hidden = YES;
    } orFailure:^(NSURLSessionDataTask *task, NSError *error) {
        weakSelf.progressContainerView.hidden = YES;
    }];
    self.progressContainerView.hidden = NO;
    [self.uploadProgressView setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask*)task animated:YES];


This method directly calls:

session = [self POST:ApiPostURL parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:mediaURI] name: ParameterMultimedia error:appendError];
        } success:^(NSURLSessionDataTask *task, id responseObject) {
            NSLog(@"%@", responseObject);
            if ([responseObject[@"status"] isEqualToString:@"ko"]) {
                NSError * error = [NSError errorWithDomain:@"it.share.auth" code:500 userInfo:nil];
                failure(task, error);
                return ;
            }
            success (task, YES);
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            NSLog(@"%@", error);
            failure(task, error);
        }];

Once I attach the progress view if I check in its method I can se that is correctly observing the task progress in the method :

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(__unused NSDictionary *)change
                       context:(void *)context <br>

The problem is that if ([object countOfBytesExpectedToSend] > 0) is always false because countOfBytesExpectedToSend is 0, reading the doc about NSURLSession Apple says that:

Discussion The URL loading system can determine the length of the upload data in three ways:

From the length of the NSData object provided as the upload body. From the length of the file on disk provided as the upload body of an upload task (not a download task). From the Content-Length in the request object, if you explicitly set it

If I print the value from the request asking the header Content-Leght I get a valid value:

"Accept-Language" = "it;q=1, en;q=0.9, fr;q=0.8, de;q=0.7, ja;q=0.6, nl;q=0.5";
"Content-Length" = 232172;
"Content-Type" = "multipart/form-data; boundary=Boundary+357828FACC07BFAC";
"User-Agent" = "XXXXX/1.0 (iPad; iOS 7.1; Scale/1.00)";

I can't get why is always returning a value equal to 0.

Googleing, githubbing, I've found the answer, essentially this is a "feature" of NSURLSession, when you create a session with uploadstream the task takes over on your setted header. Since in a stream there is not a really content size, if we set something the task will rewrite it.
Fortunately NSURLSession has two request properties currentRequest (the overriden one) and the originalRequest . The original request will continue to keep our Content-Length .
I made few mods to the UIProgressView+AFNetworking category to make it works using the set content length, it would be better another check to see if there is a stream.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(__unused NSDictionary *)change
                       context:(void *)context
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
    if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {

            //upload content length issue
//            if ([object countOfBytesExpectedToSend] > 0) {
            NSInteger byteToSend = [[[object originalRequest] valueForHTTPHeaderField:@"Content-Length"] integerValue];
            if (byteToSend){
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self setProgress:[object countOfBytesSent] / (byteToSend * 1.0f) animated:self.af_uploadProgressAnimated];
//                    [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
                });
            }
        }

        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            if ([object countOfBytesExpectedToReceive] > 0) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
                });
            }
        }

        if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
            if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
                @try {
                    [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];

                    if (context == AFTaskCountOfBytesSentContext) {
                        [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
                    }

                    if (context == AFTaskCountOfBytesReceivedContext) {
                        [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
                    }
                }
                @catch (NSException * __unused exception) {}
            }
        }
    }
#endif
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM