24

I'm trying to integrate a NSURLConnection object with UIProgressView, so I can update the user while a file download is happening in the background.

I created a separate object to download the file in the background, and I'm having problems figuring out how to update the progress property in the UIProgressView object with the correct value. It's probably something very simple, but I cannot figure it out with Googling around.

Here's the code that I have:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.resourceData setLength:0];
    self.filesize = [NSNumber numberWithLongLong:[response expectedContentLength]];
    NSLog(@"content-length: %d bytes", self.filesize);
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.resourceData appendData:data];

    NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.resourceData length]];
    NSLog(@"resourceData length: %d", [resourceLength intValue]);
    NSLog(@"filesize: %d", self.filesize);
    NSLog(@"float filesize: %f", [self.filesize floatValue]);
    progressView.progress = [resourceLength floatValue] / [self.filesize floatValue];
    NSLog(@"progress: %f", [resourceLength floatValue] / [self.filesize floatValue]);
}

As you can see, the resourceData member variable holds the file data as it is being downloaded. The filesize member variable holds the full size of the file, as returned by my web service in its Content-Length header.

It all works sort of OK, I keep getting the downloaded data with multiple executions of didReceiveData as I should, but when I try to calculate the progress value, no proper value is returned. See below for a small sample of what I get in my console log:

content-length: 4687472 bytes
resourceData length: 2904616
filesize: 4687472
float filesize: -1.000000
progress: -2904616.000000

For reference, progressView.progress is a float. filesize is a NSNumber that holds a long long. Finally, resourceLength is a NSNumber that holds a NSUInteger.

What am I missing here?

Eric Galluzzo
  • 3,191
  • 1
  • 20
  • 20
jpm
  • 16,622
  • 34
  • 63
  • 66

3 Answers3

29

Not sure what I'm missing here, but your filesize being -1 seems to be your problem. The API docs clearly state that expectedContentLength may not be available and that NSURLResponseUnknownLength is returned in these cases. NSURLResponseUnknownLength is defined as:

#define NSURLResponseUnknownLength ((long long)-1)

In these cases, you cannot get an accurate progress. You'll need to handle this and display an indeterminate progress meter of some sort.

Dave Dribin
  • 5,633
  • 3
  • 28
  • 20
  • Dave, thanks for the reply. I added an extra NSLog() call in there to illustrate my point further. I added a debug statement logging "content-length" above. – jpm Nov 23 '08 at 21:28
  • I guess I don't understand the question, then... what exactly are you asking? – Dave Dribin Nov 23 '08 at 22:24
  • I'm asking: what am I missing? Why is [self.filesize floatValue] returning -1 instead of the actual float value of its NSNumber object (which represents a long long)? Why is the division calculation in there completely broken like that? – jpm Nov 24 '08 at 00:06
  • hi dave, i have a problem that the content-length is undetermined ie -1 in my case. So can you please give me an idea on how to display this "indeterminate progress meter" that you are talking about.. thanks in advance!! :) – Zaraki Apr 18 '11 at 09:53
  • @Zaraki: You should use a UIActivityIndicatorView instead of a UIProgressView to show a spinner. – Dave Dribin Jun 30 '11 at 15:35
6

I think you are printing out the file size wrong. If self.filesize is indeed an NSNumber, you print it using the %@ format, because it is an object, not a primitive:

NSLog(@"filesize: %@", self.filesize);

By using the %d, your are just printing the pointer value of self.filesize. To print out the actual long long value, use %lli (%d is only for 32-bit values):

NSLog(@"filesize: %lli", [self.filesize longLongValue]);

So your self.filesize is actually -1 and the division is correct.

Dave Dribin
  • 5,633
  • 3
  • 28
  • 20
  • Dave, fair enough on the NSLog() mistake. However, what I really want to do is calculate a float value (from 2 NSNumber objects) to assign to progressView.progress. As you can see above, I don't get the expected value when doing a simple division. – jpm Nov 24 '08 at 04:45
  • 1
    You're doing it just fine! 2904616 / -1 = -2904616 – Dave Dribin Nov 24 '08 at 05:17
  • 1
    More specifically 2904616 / NSURLResponseUnknownLength = -2904616 – Dave Dribin Nov 24 '08 at 05:23
  • Well, as I pointed out in the "content-length" log entry, self.filesize doesn't hold -1, but rather a long long value (4687472). I don't understand why I cannot get that value to use in the division expression. That's really my *real* issue. – jpm Nov 24 '08 at 14:33
  • 1
    You're real issue is that filesize *does* hold -1 and you refuse to admit it. [self.filesize floatValue] tells you so! Try printing [self.filesize longLongValue], if you need more convincing. – Dave Dribin Nov 25 '08 at 04:20
  • 1
    Dave, you were right from the start. I wanted to apologize for my stubbornness, but I was sending the Content-Length header, the real issue was that mod_deflate was removing it before sending my file data. After a few tweaks, it's all working correctly now. Thanks! – jpm Nov 26 '08 at 14:20
0

In your code, filesize appears to be an NSNumber object (!). So

NSLog(@"filesize: %d", self.filesize);

and

NSLog(@"content-length: %d bytes", self.filesize);

will likely report something like the address (id) of that object (or something else). This is the

filesize: 4687472

you see. As pointed out by others, the file size returned by the response is indeed -1, i.e.,

NSURLResponseUnknownLength

i.e., the server did not return the file size.

Christian Fries
  • 16,175
  • 10
  • 56
  • 67