1

I've been trying to get this working for a day now and I'm still failing. I want to copy a number of files into the Documents folder of my app from the bundle when the app is installed, but this makes the user wait for a long time with the app showing the splash screen.

So I thought I'd make an initial UIAlertView with a UIProgressView as a subview that gets updated every time a file is copied into the documents folder. However the alert shows and the progress bar never gets updated. My logic was:

  • Set up the UIProgressView and UIAlertView as instance variables of my ViewController.
  • In ViewDidLoad, present the alert and set the delegate
  • In - (void)didPresentAlertView:(UIAlertView *)alertView perform a for loop that copies the files and updates the UI. the code was :

    - (void)didPresentAlertView:(UIAlertView *)alertView{
        NSString *src, *path;
        src = // path to the Bundle folder where the docs are stored //
        NSArray *docs = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:src error:nil];
    
        float total = (float)[docs count];
        float index = 1;
    
        for (NSString *filename in docs){
            path = [src stringByAppendingPathComponent:filename];
            if ([[NSFileManager defaultManager]fileExistsAtPath:path]) {
                ... // Copy files into documents folder
                [self performSelectorOnMainThread:@selector(changeUI:) withObject:[NSNumber numberWithFloat:index/total] waitUntilDone:YES];                
                index++;
            }
        }
    [alertView dismissWithClickedButtonIndex:-1 animated:YES];
    }
    

And the code for ChangeUI is

- (void) changeUI: (NSNumber*)value{
    NSLog(@"change ui %f", value.floatValue);
    [progressBar setProgress:value.floatValue];
}

However this just updates the UI from 0 to 1, although the NSLog prints all the intermediate values. Does anyone here know what I'm doing wrong?

Thanks in advance.

Andres Bucci
  • 947
  • 1
  • 9
  • 37

1 Answers1

2

The problem is that your loop is on the main thread, and thus the UI has no chance to update until the very end. Try doing the work on a background thread using GCD:

dispatch_async(DISPATCH_QUEUE_PRIORITY_DEFAULT, ^
    {
        NSString *src, *path;
        src = // path to the Bundle folder where the docs are stored //
        NSArray *docs = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:src error:nil];

        float total = (float)[docs count];
        float index = 1;

        for (NSString *filename in docs){
            path = [src stringByAppendingPathComponent:filename];
            if ([[NSFileManager defaultManager]fileExistsAtPath:path]) {
                ... // Copy files into documents folder
                dispatch_async(dispatch_get_main_queue(), ^{ [self changeUI:[NSNumber numberWithFloat:index/total]]; } );

                index++;
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{ [alertView dismissWithClickedButtonIndex:-1 animated:YES]; } );
    } );
David H
  • 40,852
  • 12
  • 92
  • 138
  • 1
    Is this the same as calling `[self performSelectorInBackground:@selector(changeUI:) withObject:[NSNumber numberWithFloat:index/total]]`? – Andres Bucci Jul 16 '12 at 11:48
  • No, not really. Look at the Description in the Apple docks on that method - you need to "properly set it up". The wave of the future is Grand Central Dispatch and blocks - Apple discourages NSThreads and they say this publicly at WWDC all the time. The dispatch above uses a thread Apple has already properly setup. GCD and ARC have made programming on Apple platforms spectacular!!! – David H Jul 16 '12 at 13:17
  • Ok, thanks @David. I've accepted the answer, now I need to look at the docs. – Andres Bucci Jul 16 '12 at 13:20
  • is the code working? I used this and gives a warning null passed to a callee that requires a non-null argument. and when I run it, I get a bad access. BTW, the performSelectorInBackground works for me. – jcesarmobile Aug 22 '12 at 08:35