iPhone SDK: formatting a numeric value with NSNumberFormatter

While working on an application that downloads a PDF document into a UIWebView, I wrote some code to display a progress bar while the user waits for the file to be downloaded. It turned out to be very simple to do this, but I hit a snag after trying to also display the amount of bytes that has been retrieved so far. Something like “0.45 Mb of 4.56 Mb”.

Problem was that by default NSNumberFormatter displays number with 3 decimal places, and I needed to customize that a bit, so I could get only 2 decimal places, and always a number before the decimal separator. For instance, getting “0.45 Mb” instead of just “.45 Mb”. The magical solution was the setPositiveFormat method, which allows the developer to specify these parameters.

Here’s the code that I ended up using, and it works great:

- (void)createProgressionAlertWithMessage:(NSString *)message {
    progressAlert = [[UIAlertView alloc] initWithTitle:message message:@"Please wait..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
    // Create the progress bar and add it to the alert
    progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(30.0f, 80.0f, 225.0f, 90.0f)];
    [progressAlert addSubview:progressView];
    [progressView setProgressViewStyle:UIProgressViewStyleBar];
 
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(90.0f, 90.0f, 225.0f, 40.0f)];
    label.backgroundColor = [UIColor clearColor];
    label.textColor = [UIColor whiteColor];
    label.font = [UIFont systemFontOfSize:12.0f];
    label.text = @"";
    label.tag = kDownloadCounterTag;
    [progressAlert addSubview:label];
 
    [progressAlert show];
    [progressAlert release];
}

That’s the method that creates the UIAlertView that will hold the progress bar, and also the UIProgressView itself to display the progress of the download. I added an extra label in there to finally display the actual bytes of the file as it is being streamed over.

For the actual download I’m using NSURLConnection so I can download the file asynchronously, and receive information while the download is progressing.

- (void)viewWillAppear:(BOOL)animated {
    NSString *file = [NSString stringWithFormat:@"http://domain.com/download?id=%@", self.docID];
    NSURL *fileURL = [NSURL URLWithString:file];
 
    NSURLRequest *req = [NSURLRequest requestWithURL:fileURL];
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
 
    [self createProgressionAlertWithMessage:@"Downloading document"];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.fileData setLength:0];
    self.totalFileSize = [NSNumber numberWithLongLong:[response expectedContentLength]];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.fileData appendData:data];
 
    NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.fileData length]];
    NSNumber *progress = [NSNumber numberWithFloat:([resourceLength floatValue] / [self.totalFileSize floatValue])];
    progressView.progress = [progress floatValue];
 
    const unsigned int bytes = 1024 * 1024;
    UILabel *label = (UILabel *)[progressAlert viewWithTag:kDownloadCounterTag];
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [formatter setPositiveFormat:@"##0.00"];
    NSNumber *partial = [NSNumber numberWithFloat:([resourceLength floatValue] / bytes)];
    NSNumber *total = [NSNumber numberWithFloat:([self.totalFileSize floatValue] / bytes)];
    label.text = [NSString stringWithFormat:@"%@ MB of %@ MB", [formatter stringFromNumber:partial], [formatter stringFromNumber:total]];
    [formatter release];
}
 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [progressAlert dismissWithClickedButtonIndex:0 animated:YES];
}

I’m hoping this is useful to someone else. Let me know if there’s a better to do some of the things I’m doing here.

3 Comments »

  1. Waqas Qureshi said,

    April 20, 2009 @ 5:07 am

    Hi, I used this code of snippet in my app but it doesn’t show exact downloading percentage, so can you tell me the exact value of kDownloadCounterTag, means what would you set the default value.

  2. Harry said,

    June 10, 2009 @ 8:14 am

    @Waqas Just set it to 1.

  3. Enea said,

    August 20, 2009 @ 7:49 pm

    Hello i have an app already and Im pretty new at iphone development if i were to just copy and past your code into my view controller would that work? i cant seem to figure out how to make a simple app just for this. If you could help that would be a big relief.

RSS feed for comments on this post · TrackBack URI

Leave a Comment