A modern download manager for iOS (Objective C) based on NSURLSession to deal with asynchronous downloading, management and persistence of multiple files.
TWRDownloadManager is a singleton instance and can thus be called in your code safely from wherever you need to. The idea of writing yet another download manager library stemmed from the fact that at the time of the writing (and yet still) there were no available open source projects based on the new NSURLSession
APIs made available by Apple in iOS 7.
TWRDownloadManager leverages the power of NSURLSession
and NSURLSessionDownloadTask
to make downloading of files and keeping track of their progress a breeze.
09.22.2014 - UPDATE!!!
v1.0.0 of TWRDownloadManager
now supports background modes. The API has changed so it’s not backwards compatible, hence its bump to v1.0.0. See the documentation below for further information.
A demo project has also been added to showcase the use of the download manager in its simplest form.
v1.1.0 of TWRDownloadManager
adds the ability to pass a block when creating the download to keep track of an estimated remaining download time. The algorithm can definitely be improved but it works.
Updated demo project.
To use the library, just add the dependency to your Podfile
:
platform :ios
pod 'TWRDownloadManager'
Run pod install
to install the dependencies.
Next, import the header file wherever you want to use the manager:
#import <TWRDownloadManager/TWRDownloadManager.h>
Since TWRDownloadManager is a singleton you could import it in the .pch
file of your project so that it can be accessed and used wherever you need it without worrying about importing it in each of your classes.
TWRDownloadManager
provides facilities for the following task:
- downloading files;
- persisting downloaded files and saving them to disk;
- keeping track of download progress via block syntax;
- being notified of the download completion via block syntax;
- deleting downloaded files;
- checking for file existence.
All the following instance methods can be called directly on [TWRDownloadManager sharedManager]
.
- (void)downloadFileForURL:(NSString *)url
withName:(NSString *)fileName
inDirectoryNamed:(NSString *)directory
progressBlock:(void(^)(CGFloat progress))progressBlock
completionBlock:(void(^)(BOOL completed))completionBlock
enableBackgroundMode:(BOOL)backgroundMode;
- (void)downloadFileForURL:(NSString *)url
inDirectoryNamed:(NSString *)directory
progressBlock:(void(^)(CGFloat progress))progressBlock
completionBlock:(void(^)(BOOL completed))completionBlock
enableBackgroundMode:(BOOL)backgroundMode;
- (void)downloadFileForURL:(NSString *)url
progressBlock:(void(^)(CGFloat progress))progressBlock
completionBlock:(void(^)(BOOL completed))completionBlock
enableBackgroundMode:(BOOL)backgroundMode;
The easiest way to get started is by simply passing to the last of the aforementioned methods the URL string of the file that needs to be downloaded. You will get a chance to pass in two blocks that will help you keep track of the download progress (a float from 0 to 1) and of the completion of the task.
All the files, once downloaded will be moved from the /tmp
directory of the device to the Caches directory. This is done for two reasons:
- the
/tmp
directory can be cleaned once in a while to make sure that any partial, cancelled or failed downloads get properly disposed of and do not occupy space both on the device and in iTunes backups; - the Caches directory is not synced by default with the user's iCloud documents. This is in compliance with Apple's rules about content that – not being user-specific – can be re-downloaded from the internet and should not be synced with iCloud.
If a directory name is provided, a new sub-directory will be created in the Cached directory.
Once the file is finished downloading, if a name was provided by the user, it will be used to store the file in its final destination. If no name was provided the manager will use by default the last path component of the URL string (e.g. for http://www.example.com/files/my_file.zip
, the final file name would be my_file.zip
).
To check if a file is being downloaded, you can use one of the following methods:
- (BOOL)isFileDownloadingForUrl:(NSString *)url withProgressBlock:(void(^)(CGFloat progress))block;
- (BOOL)isFileDownloadingForUrl:(NSString *)url withProgressBlock:(void(^)(CGFloat progress))block completionBlock:(void(^)(BOOL completed))completionBlock;
As with the previous download methods, you get a chance to be called back for progress and completion.
To retrieve a list of current files being downloaded, you can use the following:
- (NSArray *)currentDownloads;
This method returns an array of NSString
objects with the URLs of the current downloads being performed.
The downloads, which are uniquely referenced by the download manager by the provided URL, can either be canceled singularly or all together with a single call via one of the two following methods:
- (void)cancelAllDownloads;
- (void)cancelDownloadForUrl:(NSString *)urlString;
TWRDownloadManager also provides some facilities to deal with downloaded files.
You can check for existence...
- (BOOL)fileExistsForUrl:(NSString *)urlString;
- (BOOL)fileExistsForUrl:(NSString *)urlString inDirectory:(NSString *)directoryName;
- (BOOL)fileExistsWithName:(NSString *)fileName;
- (BOOL)fileExistsWithName:(NSString *)fileName inDirectory:(NSString *)directoryName;
...and retrieve the file location with the following ones:
- (NSString *)localPathForFile:(NSString *)fileIdentifier;
- (NSString *)localPathForFile:(NSString *)fileIdentifier inDirectory:(NSString *)directoryName;
Downloaded files can be deleted via the following methods:
- (BOOL)deleteFileForUrl:(NSString *)urlString;
- (BOOL)deleteFileForUrl:(NSString *)urlString inDirectory:(NSString *)directoryName;
- (BOOL)deleteFileWithName:(NSString *)fileName;
- (BOOL)deleteFileWithName:(NSString *)fileName inDirectory:(NSString *)directoryName;
To enable background downloads in iOS 7+, you should conform to the following steps:
- enable background modes in your project. Select the project in the Project Navigator in Xcode, select your target, then select the
Capabilities
tab and finally enable Background Modes:
- add the following method to your AppDelegate
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
[TWRDownloadManager sharedManager].backgroundTransferCompletionHandler = completionHandler;
}
- register for local notifications in your
application:didFinishLaunchingWithOptions:
so that you can display a message to the user when the download completes:
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]];
}
TWRDownloadManager
, being able to keep track of multiple downloads at once, could be used to show a list of current downloads inside a table view.
These are just a couple of suggestions on how it could be achieved by using TWRDownloadManager
.
In your UITableViewCell
subclass, import <TWRDownloadManager/TWRDownloadObject.h>
.
Define your progress and completion blocks as two properties:
@property (strong, nonatomic) TWRDownloadProgressBlock progressBlock;
@property (strong, nonatomic) TWRDownloadCompletionBlock completionBlock;
In your implementation (.m) file, define their block getters:
- (TWRDownloadProgressBlock)progressBlock {
__weak typeof(self)weakSelf = self;
return ^void(CGFloat progress){
dispatch_async(dispatch_get_main_queue(), ^(void) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
// do something with the progress on the cell!
});
};
}
- (TWRDownloadCompletionBlock)completionBlock {
__weak typeof(self)weakSelf = self;
return ^void(BOOL completed){
dispatch_async(dispatch_get_main_queue(), ^(void) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
// do something
});
};
}
Finally, don't forget to nil out the blocks before the cell can be reused:
-(void)prepareForReuse {
self.progressBlock = nil;
}
Now in your code, whenever you set up a new cell you can get the cell's own progress and completion block and pass them to the download manager. Voilà!
TWRDownloadManager
requires iOS 7.x or greater.
Usage is provided under the MIT License. See LICENSE for the full details.
All contributions are welcome. Please fork the project to add functionalities and open a pull request to have them merged into the master branch in the next releases.