4

in my iPhone app, I am using the default NSFileManager to copy and move some data around. The amount of data may be some MB, so it may take several seconds to finish copying. If the user now starts a copy process and quits the application, can I continue writing to disk in background mode? If so, how? I do not want to start a new copy process, but only finish the running one.

I am currently using two kinds of method calls:

[imageData writeToFile:path atomically:YES];

and

[fm copyItemAtPath:sourcePath toPath:destinationPath error:&error];

Both calls are wrapped in another method and are performed in the background via -performSelectorInBackground:withObject:.

Another question is if I can get any information on how far the writing operation has progressed or at least if it has already finished?

EDIT: Some more information: Currently, I did not implement any background tasks at all. What happens to running disk operations when the user presses the home button? Are they just cut off and the file is incomplete? Can I continue writing on the next start? Are they canceled and the incomplete file is removed?

To sum it all up: I am writing data to disk and I want it to stay consistent when the user presses the home button during a writing operation. How do I achieve this?

Chris Hanson
  • 54,380
  • 8
  • 73
  • 102
Björn Marschollek
  • 9,899
  • 9
  • 40
  • 66

2 Answers2

4

By default you app does not really finishes when the user press the home button. Hence it should finish the that task as long is does not take too long. If it takes long then please take a look at this question: How to implement Task completion

One thing: I think you are confused about what performSelectorInBackground:withObject: really does. The background used in "... I continue writing to disk in background mode" and the background in "performSelectorIn Background :withObject: " are not the same background

The former background: Is when you app becomes invisible to the user, but is still running, at least for a while. (When the user presses twice the home button and change to another app)

The latter background: Refers to a background thread, which is the opposite to the main thread.

In this case, if you use or not performSelectorInBackground:withObject: it will have no effect in whether you app can do it background mode or not. These are completely different things

You can set BOOL finished = YES right after [fm copyItemAtPath:ToPath:error:]; and save it in NSUserDefaults and check that flag when your app comes to the foreground again ;)

Hope it helps ;)

Community
  • 1
  • 1
nacho4d
  • 43,720
  • 45
  • 157
  • 240
  • Backgorund in either context is absolutely clear to me. ;) I just added the information for it to be complete. It might matter for my latter question regarding the progress. Setting a BOOL does not really solve my problem, because there may be multiple disk operations at a time. – Björn Marschollek Jan 03 '11 at 09:21
  • @Björn use an int instead of a BOOL to count outstanding operations? – David Gelhar Jan 26 '11 at 03:06
  • Read this: http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html%23//apple_ref/doc/uid/TP40007072-CH5-SW12 it says the system will give you at most 30 seconds of life ;) See: beginBackgroundTaskWithExpirationHandler: method from here: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html In the expiration Handler I would check: What % was copied? 100% 80%. – nacho4d Jan 26 '11 at 08:33
  • In 30 sec you can write hundreds of MB (if not more)... that time should be enough for you ;) – nacho4d Jan 26 '11 at 08:35
4

You may want to look into NSOperationQueue, the documentation page is here.

Also there are two existing Stack Overflow questions that address using NSOperationQueue that are here and here. Both have accepted answers.

As for figuring out whether a file write has completed, I had this issue as well when I was writing an OS X application that created a very large file. I wanted the user to be able to track the progress of the write. I ended up using an NSTimer along with a UIProgressBar

Essentially, you will want to determine the (expected) total size of the file. Then, as you write the file, you should have another method (in the code below I have checkFileWritingProgress) that you can call at periodic intervals using an NSTimer. Check the current progress against the total expected file size and update the UIProgressBar accordingly.

I have provided some code to get you started.

- (void)checkFileWritingProgress:(NSTimer *)someTimer {
    fileAttributes = [fileManager attributesOfItemAtPath:[NSString stringWithFormat:@"%@.data",saveLocation] error:nil];
    currentFileSize = [fileAttributes fileSize]; // instance variable
    if(currentFileSize < maxFileSize) { // maxFileSize is instance variable
        [progressBar setDoubleValue:(((double)currentFileSize/(double)maxFileSize)*100)];
            // progressWindows OS X only... not on iOS
        //[progressWindow setTitle:[NSString stringWithFormat:@"Writing... | %.0f%%",(((double)currentFileSize/(double)maxFileSize)*100)]];
    }
    else {
        [progressBar setDoubleValue:100.0];
    }
}

And the timer...

NSTimer *timer = [[NSTimer scheduledTimerWithTimeInterval:0.01
                                                          target:self
                                                        selector:@selector(checkFileWritingProgress:)
                                                        userInfo:nil
                                                         repeats:YES] retain];

//(linked to timer... this is the 
// CORRECT way to use a determinate progress bar)

[progressBar setIndeterminate:NO];
[progressBar setDoubleValue:0.0];
[progressBar displayIfNeeded];

I hope this code helps. Let me know if anything needs to be clarified.

Community
  • 1
  • 1
aqua
  • 3,269
  • 28
  • 41