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.