2

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.

Andrea
  • 26,120
  • 10
  • 85
  • 131

1 Answers1

2

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
}
Andrea
  • 26,120
  • 10
  • 85
  • 131
  • It works... but is not good change like that, you already report to @mattt? (AFNetworking) – silvaric Sep 08 '14 at 00:12
  • I did as you can see here https://github.com/AFNetworking/AFNetworking/issues/1398 and https://github.com/AFNetworking/AFNetworking/issues/1354 . Mattt has replied that he doesn't support this kind of mod. – Andrea Sep 08 '14 at 06:34
  • 1
    And here https://github.com/AFNetworking/AFNetworking/issues/2027 . I totally understand Mattt point, because is a really untested mod in respect of all the test units of AFNetworking. I can tell you that I'm using it from about 6 month with success. – Andrea Sep 08 '14 at 06:41