How to show the progress of copying a large file in iOS?

I am writing an iOS app. In my app, I want to copy some files from one folder to another. But because some files is too large, it will take a long time to finish the copy. So I want to add a progress bar to show the percentage of the copy. But I find that the file manager has no callback method to get the percentage. Does anyone have the good solution to it?

Answers


In high level :

  1. Run your copying process in a seperate thread (T1)
  2. Run another thread (T2) which reads periodically (say every 100ms) the destination file current_size.
  3. Calculate the percentage : current_size / total_size
  4. Update you progress bar ui element

I've created a simple class with @giorashc approach.

If anyone needs something like this feel free to use it.

The .h

#import <UIKit/UIKit.h>

@protocol IDCopyUtilsDelegate;

@interface IDCopyUtils : NSObject

@property (nonatomic, weak) id<IDCopyUtilsDelegate> delegate;

- (void)copyFileAtPath:(NSString *)sourcePath toPath:(NSString *)targetPath;

@end

// 3. Definition of the delegate's interface
@protocol IDCopyUtilsDelegate <NSObject>

- (void)setCopyProgress:(float)progress;
- (void)didFinishedCopyWithError:(NSError *)error;

@end

The .m

#import "IDCopyUtils.h"

@interface IDCopyUtils()

@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSString *sourcePath;
@property (nonatomic, strong) NSString *targetPath;

@end

@implementation IDCopyUtils

- (void)copyFileAtPath:(NSString *)sourcePath toPath:(NSString *)targetPath
{
    self.sourcePath = sourcePath;
    self.targetPath = targetPath;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;

    if ([fileManager fileExistsAtPath:self.targetPath] == YES) {
        [fileManager removeItemAtPath:self.targetPath error:&error];
    }

    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.100
                                     target:self
                                   selector:@selector(checkFileSize)
                                   userInfo:nil
                                    repeats:YES];

    [self performSelector:@selector(startCopy) withObject:nil afterDelay:0.5];

}

- (void)checkFileSize
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *attributesSource = [[NSFileManager defaultManager] attributesOfItemAtPath:self.sourcePath error:NULL]; unsigned long long fileSize = [attributesSource fileSize];

        NSDictionary *attributesTarget = [[NSFileManager defaultManager] attributesOfItemAtPath:self.targetPath error:NULL]; unsigned long long fileSizeTarget = [attributesTarget fileSize];

        double progress = (float)fileSizeTarget / (float)fileSize;

        if (self.delegate && [self.delegate respondsToSelector:@selector(setCopyProgress:)])
        {
            [self.delegate setCopyProgress:progress];
        }

        NSLog(@"Size: %f", progress);
    });
}

- (void)startCopy
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSError *error;

        if ([fileManager fileExistsAtPath:self.targetPath] == YES) {
            [fileManager removeItemAtPath:self.targetPath error:&error];
        }

        if ([fileManager fileExistsAtPath:self.targetPath] == NO) {
            [fileManager copyItemAtPath:self.sourcePath toPath:self.targetPath error:&error];

            [self.timer invalidate];
            self.timer = nil;

            if (self.delegate && [self.delegate respondsToSelector:@selector(didFinishedCopyWithError:)])
            {
                [self.delegate didFinishedCopyWithError:error];
            }
        }
    });
}

@end

You can use it like this (for example):

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"iso"];
NSString *targetPath = [documentsDirectory stringByAppendingPathComponent:@"test.iso"];

IDCopyUtils *copyUtils = [[IDCopyUtils alloc] init];
copyUtils.delegate = self;
[copyUtils copyFileAtPath:sourcePath toPath:targetPath];

And you will we able to update you progress view and get notified when the file did finidhed copying using the delegate methods.


Need Your Help

class not found exception while running my camera with face detection project

java android eclipse

I get a class not found exception. I think this error comes when there is a library issue. I am implementing face detection in my camera for that i used latest google play services library and appc...

How To Create Per-Request Singleton in Pylons?

python singleton pylons

In our Pylons based web-app, we're creating a class that essentially provides some logging functionality. We need a new instance of this class for each http request that comes in, but only one per