Skip to content

EasyDownloadSession allows to pause and resume downloads having a full control of the order of execution.

License

Notifications You must be signed in to change notification settings

lagubull/EasyDownloadSession

Repository files navigation

Build Status Version License Platform CocoaPods Readme Score

EasyDownloadSession allows pausing and resuming downloads, giving the developer full control of the tasks execution.

##Installation via Cocoapods

To integrate EasyDownloadSession into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

pod 'EasyDownloadSession'

Then, run the following command:

$ pod install

CocoaPods 0.39.0+ is required to build EasyDownloadSession.

##Usage

EasyDownload Session is built on top of NSURLSession, contains a wrap around the session object that will take care of scheduling new download tasks for you.

It can be configured to run as many concurrent operations as needed but the default behaviour is 1 running task. It should be noted that this pod will help also for uploading but for conveniency we will refer to all transfers as downloads.

IMPORTANT: maximum number of downloads per host is 100.

Cache has been disabled.

There are two ways of adding new downloads:

  • scheduleDownloadWithId: It will add the task to a stack and will run whenever there is a free download slot.

  • forceDownloadWithId: it will start the task immediately, pausing other download if necessary.

We can find a good example of the usage of this code in [email protected]:lagubull/SocialChallenge.git

Where we are using EasyDownload Session to add requests for downloading large images that will be afterwards processed and displayed in a tableview, therefore we use the stack provided by this pod as we are more interested in newer requests than we are in old ones.

####Stacks

EasyDownloadSession can manage as many stacks as you may need, before scheduling any download you should create a stack and register it in the session.

		let mediaStack = Stack()
        
        DownloadSession.sharedInstance.registerStack(stack: mediaStack,
                                                     stackIdentifier:kJSCMediaDownloadStack)

####Schedule

We add the requests to the stack respecting the current download task.

After a download has finished successfully, A success block will be triggered. If the download finishes failure block will be triggered. Additionally, the progress block will be called to notify the progress of the task.

class func retrieveMediaForPost(post: JSCPost, retrievalRequired: ((postId: String) -> Void)?, success: ((result: AnyObject?, postId: String) -> Void)?, failure: ((error: NSError?, postId: String) -> Void)?) {
        
        let mySuccess = success
        let myFailure = failure
        
        if post.userAvatarRemoteURL != nil {
            
            let operation = JSCLocalImageAssetRetrievalOperation.init(postId: post.postId!)
            
            operation.onCompletion = { JSCOperationOnCompletionCallback in
                
                if let imageMedia = JSCOperationOnCompletionCallback {
                    
                    mySuccess?(result: imageMedia,
                               postId: post.postId!)
                }
                else {
                    
                    retrievalRequired?(postId: post.postId!)
                    
                    DownloadSession.scheduleDownloadWithId(post.postId!,
                                                           fromURL: NSURL.init(string: post.userAvatarRemoteURL!)!,
                                                           stackIdentifier: kJSCMediaDownloadStack,
                                                           progress: nil,
                                                           success: { (taskInfo: DownloadTaskInfo!, responseData: NSData?) -> Void in
                                                            
                                                            let storeOperation = JSCMediaStorageOperation.init(postId: post.postId!, data: responseData)
                                                            
                                                            storeOperation.onSuccess = { JSCOperationOnSuccessCallback in
                                                                
                                                                if let imageMedia = JSCOperationOnSuccessCallback {
                                                                    
                                                                    mySuccess?(result: imageMedia, postId: post.postId!)
                                                                }
                                                                else {
                                                                    
                                                                    myFailure?(error: nil, postId: post.postId!)
                                                                }
                                                            }
                                                            
                                                            storeOperation.onFailure = { JSCOperationOnFailureCallback in
                                                                
                                                                myFailure?(error: nil, postId: post.postId!)
                                                            }
                                                            
                                                            storeOperation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
                                                            
                                                            JSCOperationCoordinator.sharedInstance.addOperation(storeOperation)
                        },
                                                           failure: { (taskInfo, error) -> Void in
                                                            
                                                            myFailure?(error: error, postId: post.postId!)
                    })
                }
            }
            
            operation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
            
            JSCOperationCoordinator.sharedInstance.addOperation(operation)
        }
        else {
            
            myFailure?(error: nil,
                       postId: post.postId!)
        }
    }
#pragma mark - Retrieval

+ (void)retrieveMediaForPost:(JSCPost *)post
           retrievalRequired:(void (^)(NSString *postId))retrievalRequired
                     success:(void (^)(id result, NSString *postId))success
                     failure:(void (^)(NSError *error, NSString *postId))failure;
{
 [EDSDownloadSession scheduleDownloadWithId:post.postId
                         	        fromURL:[NSURL URLWithString:post.userAvatarRemoteURL]
									progress:nil
									 success:^(EDSDownloadTaskInfo *downloadTask,
									           NSData *responseData)
                 {
                     JSCMediaStorageOperation *storeOPeration =
                      [[JSCMediaStorageOperation alloc] initWithPostID:post.postId
                                                                  data:responseData];
                     
                     storeOPeration.onSuccess = ^(id result)
                     {
                         if (result)
                         {
                             if (success)
                             {
                                 success(result, post.postId);
                             }
                         }
                         else
                         {
                             if (failure)
                             {
                                 failure(nil, post.postId);
                             }
                         }
                     };
                     
                     storeOPeration.onFailure = ^(NSError *error)
                     {
                         if (failure)
                         {
                             failure(error, post.postId);
                         }
                     };
                     
                     storeOPeration.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier;
                     
                     [[JSCOperationCoordinator sharedInstance] addOperation:storeOPeration];
                 }
                                                   failure:^(EDSDownloadTaskInfo *downloadTask, NSError *error)
                 {
                     if (failure)
                     {
                         failure(error, post.postId);
                     }
                 }];
}

An alternative signature for a single completion block is available

  DownloadSession.scheduleDownloadWithId(post.postId!,
                                                           fromURL: NSURL.init(string: post.userAvatarRemoteURL!)!,
                                                           stackIdentifier: kJSCMediaDownloadStack,
                                                           progress: nil,
                                                           completion: { (taskInfo: DownloadTaskInfo!, responseData: NSData?, error: NSError?) -> Void in
                                                            
                                                            if error == nil {
                                                                
                                                                let storeOperation = JSCMediaStorageOperation.init(postId: post.postId!, data: responseData)
                                                                
                                                                storeOperation.onSuccess = { JSCOperationOnSuccessCallback in
                                                                    
                                                                    if let imageMedia = JSCOperationOnSuccessCallback {
                                                                        
                                                                        mySuccess?(result: imageMedia, postId: post.postId!)
                                                                    }
                                                                    else {
                                                                        
                                                                        myFailure?(error: nil, postId: post.postId!)
                                                                    }
                                                                }
                                                                
                                                                storeOperation.onFailure = { JSCOperationOnFailureCallback in
                                                                    
                                                                    myFailure?(error: nil, postId: post.postId!)
                                                                }
                                                                
                                                                storeOperation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
                                                                
                                                                JSCOperationCoordinator.sharedInstance.addOperation(storeOperation)
                                                            }
                                                            else {
                                                                
                                                                myFailure?(error: error, postId: post.postId!)
                                                            }
                    })
                }
            }

####Force Download

This will effectively pause all downloads start this one and then start any other download that can up to maxDownloads.

After a download has finished successfully, A success block will be triggered. If the download finishes failure block will be triggered.

Additionally, the progress block will be called to notify the progress of the task.

class func retrieveMediaForPost(post: JSCPost, retrievalRequired: ((postId: String) -> Void)?, success: ((result: AnyObject?, postId: String) -> Void)?, failure: ((error: NSError?, postId: String) -> Void)?) {
        
        let mySuccess = success
        let myFailure = failure
        
        if post.userAvatarRemoteURL != nil {
            
            let operation = JSCLocalImageAssetRetrievalOperation.init(postId: post.postId!)
            
            operation.onCompletion = { JSCOperationOnCompletionCallback in
                
                if let imageMedia = JSCOperationOnCompletionCallback {
                    
                    mySuccess?(result: imageMedia,
                               postId: post.postId!)
                }
                else {
                    
                    retrievalRequired?(postId: post.postId!)
                    
                    DownloadSession.forceDownloadWithId(post.postId!,
                                                        fromURL: NSURL.init(string: post.userAvatarRemoteURL!)!,
                                                        stackIdentifier: kJSCMediaDownloadStack,
                                                        progress: nil,
                                                        success: { (taskInfo: DownloadTaskInfo!, responseData: NSData?) -> Void in
                                                            
                                                            let storeOperation = JSCMediaStorageOperation.init(postId: post.postId!, data: responseData)
                                                            
                                                            storeOperation.onSuccess = { JSCOperationOnSuccessCallback in
                                                                
                                                                if let imageMedia = JSCOperationOnSuccessCallback {
                                                                    
                                                                    mySuccess?(result: imageMedia, postId: post.postId!)
                                                                }
                                                                else {
                                                                    
                                                                    myFailure?(error: nil, postId: post.postId!)
                                                                }
                                                            }
                                                            
                                                            storeOperation.onFailure = { JSCOperationOnFailureCallback in
                                                                
                                                                myFailure?(error: nil, postId: post.postId!)
                                                            }
                                                            
                                                            storeOperation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
                                                            
                                                            JSCOperationCoordinator.sharedInstance.addOperation(storeOperation)
                        },
                                                        failure: { (taskInfo, error) -> Void in
                                                            
                                                            myFailure?(error: error, postId: post.postId!)
                    })
                }
            }
            
            operation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
            
            JSCOperationCoordinator.sharedInstance.addOperation(operation)
        }
        else {
            
            myFailure?(error: nil,
                       postId: post.postId!)
        }
    }
#pragma mark - Retrieval

+ (void)retrieveMediaForPost:(JSCPost *)post
           retrievalRequired:(void (^)(NSString *postId))retrievalRequired
                     success:(void (^)(id result, NSString *postId))success
                     failure:(void (^)(NSError *error, NSString *postId))failure;
{
[EDSDownloadSession forceDownloadWithId:post.postId
                         	    fromURL:[NSURL URLWithString:post.userAvatarRemoteURL]
							   progress:nil
							    success:^(EDSDownloadTaskInfo *downloadTask,
									           NSData *responseData)
                 {
                     JSCMediaStorageOperation *storeOPeration =
                      [[JSCMediaStorageOperation alloc] initWithPostID:post.postId
                                                                  data:responseData];
                     
                     storeOPeration.onSuccess = ^(id result)
                     {
                         if (result)
                         {
                             if (success)
                             {
                                 success(result, post.postId);
                             }
                         }
                         else
                         {
                             if (failure)
                             {
                                 failure(nil, post.postId);
                             }
                         }
                     };
                     
                     storeOPeration.onFailure = ^(NSError *error)
                     {
                         if (failure)
                         {
                             failure(error, post.postId);
                         }
                     };
                     
                     storeOPeration.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier;
                     
                     [[JSCOperationCoordinator sharedInstance] addOperation:storeOPeration];
                 }
                                                   failure:^(EDSDownloadTaskInfo *downloadTask, NSError *error)
                 {
                     if (failure)
                     {
                         failure(error, post.postId);
                     }
                 }];
}

An alternative signature for a single completion block is available

DownloadSession.forceDownloadWithId(post.postId!,
                                                        fromURL: NSURL.init(string: post.userAvatarRemoteURL!)!,
                                                        stackIdentifier: kJSCMediaDownloadStack,
                                                        progress: nil,
                                                        completion: { (taskInfo: DownloadTaskInfo!, responseData: NSData?, error: NSError?) -> Void in
                                                            
                                                            if error == nil {
                                                                
                                                                let storeOperation = JSCMediaStorageOperation.init(postId: post.postId!, data: responseData)
                                                                
                                                                storeOperation.onSuccess = { JSCOperationOnSuccessCallback in
                                                                    
                                                                    if let imageMedia = JSCOperationOnSuccessCallback {
                                                                        
                                                                        mySuccess?(result: imageMedia, postId: post.postId!)
                                                                    }
                                                                    else {
                                                                        
                                                                        myFailure?(error: nil, postId: post.postId!)
                                                                    }
                                                                }
                                                                
                                                                storeOperation.onFailure = { JSCOperationOnFailureCallback in
                                                                    
                                                                    myFailure?(error: nil, postId: post.postId!)
                                                                }
                                                                
                                                                storeOperation.targetSchedulerIdentifier = kJSCLocalDataOperationSchedulerTypeIdentifier
                                                                
                                                                JSCOperationCoordinator.sharedInstance.addOperation(storeOperation)
                                                            }
                                                            else {
                                                                
                                                                myFailure?(error: error, postId: post.postId!)
                                                            }
                    })
                }
            }

####Concurrent Downloads

You can use the property maxDownloads to increase the number of concurrent downloads.

	mediaStack.maxDownloads = 4
    [EDSDownloadSession downloadSession].maxDownloads = @(4);

####Pause

Pausing a download stops the task and stores the progress in memory so that it can be resumed later on.

All stacks may be paused at any time:

	DownloadSession.pauseDownloads()

Specific stacks may be stopped just as easily:

	DownloadSession.pauseDownloadsInStack(kJSCMediaDownloadStack)

####Resume

After a download has been manually paused, it can be resumed and it will continue from whenever it had been stopped.

To restart all downloads (within the stablished limits) you may call:

	DownloadSession.resumeDownloads()

To resume an specific stack, however:

	DownloadSession.resumeDownloadsInStack(kJSCMediaDownloadStack)

####Cancel

All tasks may be cancelled at any time:

	DownloadSession.cancelDownloads()

Specific tasks may be stopped just as well:

	 DownloadSession.cancelDownload(post.postId!,
                                    stackIdentifier: kJSCMediaDownloadStack)

##Found an issue?

Please open a new Issue here if you run into a problem specific to EasyAlert, have a feature request, or want to share a comment.

Pull requests are encouraged and greatly appreciated! Please try to maintain consistency with the existing coding style. If you're considering taking on significant changes or additions to the project, please communicate in advance by opening a new Issue. This allows everyone to get onboard with upcoming changes, ensures that changes align with the project's design philosophy, and avoids duplicated work.

Thank you!

About

EasyDownloadSession allows to pause and resume downloads having a full control of the order of execution.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages