WebServices solution for iPhone - Second Stage
- The FileDownloadController class will create and manage all needed FileDownload objects
- Whenever an object needs to download a file, it should get the FileDownloadController instance and then asks for the download
- Once the download is complete, or in case of error, a callback will be received by the requesting object
- Store requests in a queue and attend them once a slot is available
- Cancel the oldest download and use it for the requested
@interface FileDownloadControllerItem : NSObject
{
int jobID;
FileDownload * downloadObject;
NSDate * startTime;
}
@property (nonatomic, retain) FileDownload * downloadObject;
@property (nonatomic, assign) int jobID;
@property (nonatomic, retain) NSDate * startTime;
-(FileDownloadControllerItem *)initwithDelegate:(id)delegate;
-(void)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id<FileDownloadProtocol>)delegate;
-(void)stopDownload;
@end
- jobID it holds the Download ID discussed in the previous post of this series.
- downloadObject holds the FileDownload object that will be used to process the download
- startTime holds the time when the download was called and will be used to identify the older download when a new download is requested and there are no available slots
-(FileDownloadControllerItem *)initwithDelegate:(id)delegate;
-(void)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id<FileDownloadProtocol>)delegate;
-(void)stopDownload;
/**
Initializtion given a delegate
**/
-(FileDownloadControllerItem *)initwithDelegate:(id)delegate
{
[self init];
self.jobID = FREESLOT;
self.downloadObject = [[FileDownload alloc] init];
self.startTime = [NSDate dateWithTimeIntervalSince1970:0];
return self;
}
/**
Download a file
**/
-(void)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id)delegate;
{
self.jobID = fileID;
self.startTime = [NSDate dateWithTimeIntervalSinceNow:0];
[self.downloadObject setJobID:fileID];
[self.downloadObject startDownloadFile:name callBack:delegate];
}
/**
Cancel current download
**/
-(void)stopDownload
{
[self.downloadObject stopDownload];
}
@interface FileDownloadController : NSObject
{
int currentDownloads;
BOOL maintainQueue;
int totalJobs;
NSMutableArray * downloadArray;
NSMutableDictionary * callBackObjects;
}
@property (nonatomic, assign) int totalJobs;
@property (nonatomic, assign) BOOL maintainQueue;
+ (FileDownloadController *)getController;
+ (FileDownloadController *)getController:(NSInteger)jobs useQueue:(BOOL)queue;
- (FileDownloadController *)initWithJobs:(NSInteger)jobs;
- (FileDownloadController *)initWithJobs:(NSInteger)jobs useQueue:(BOOL)queue;
- (BOOL)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id<FileDownloadProtocol>)delegate;
- (BOOL)findJobInQueue:(NSInteger)job;
- (void)cancelDownloadFile:(NSInteger)fileID;
- (void)stopAllDownloads;
@end
- currentDownloads is used to keep track of the actual concurrent downloads
- maintainQueue is not used, yet, but it intention is to identify if a download queue is used or if the older download might be cancelled
- totalJobs maximum number of concurrent downloads
- downloadArray this array holds all the download slots
- callBackObjects is used to hold the download ids and the callback objects; this is needed because the FileDownload will call a callback within the controller, then the controller will call the requesting object
+ (FileDownloadController *)getController;
+ (FileDownloadController *)getController:(NSInteger)jobs useQueue:(BOOL)queue;
- (FileDownloadController *)initWithJobs:(NSInteger)jobs;
- (FileDownloadController *)initWithJobs:(NSInteger)jobs useQueue:(BOOL)queue;
- (BOOL)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id<FileDownloadProtocol>)delegate;
- (BOOL)findJobInQueue:(NSInteger)job;
- (void)cancelDownloadFile:(NSInteger)fileID;
- (void)stopAllDownloads;
/**
Get Singleton object
**/
+ (FileDownloadController *)getController
{
if (controller == nil)
{
controller = [[FileDownloadController alloc] initWithJobs:10 useQueue:NO];
}
return controller;
}
/**
Get Singleton object
**/
+ (FileDownloadController *)getController:(NSInteger)jobs useQueue:(BOOL)queue
{
if (controller == nil)
{
controller = [[FileDownloadController alloc] initWithJobs:jobs useQueue:queue];
}
return controller;
}
/**
Initialization with job number
**/
-(FileDownloadController *)initWithJobs:(NSInteger)jobs
{
return [self initWithJobs:jobs useQueue:NO];
}
/**
Initialization with jobs and Queue handling
**/
-(FileDownloadController *)initWithJobs:(NSInteger)jobs useQueue:(BOOL)queue
{
[self init];
totalJobs = jobs;
maintainQueue = queue;
currentDownloads = 0;
downloadArray = [[NSMutableArray alloc] initWithCapacity:self.totalJobs];
for (int i = 0; i < self.totalJobs; ++i)
{
[downloadArray addObject:[[FileDownloadControllerItem alloc] initwithDelegate:self]];
}
callBackObjects = [[NSMutableDictionary alloc] init];
return self;
}
/**
Download files
**/
-(BOOL)downloadFile:(NSInteger)fileID fileName:(NSString *)name callBack:(id<FileDownloadProtocol>)delegate
{
// Validate file is not already in download queue, cero is not valid id
// Find a free download slot
// If none, clear one
// Configure slot to download file
BOOL error;
FileDownloadControllerItem * downloadItem;
error = [self findJobInQueue:fileID];
if (!error)
{
[self storeSession:fileID callBack:delegate];
downloadItem = [self getFreeSlot:NO];
if (downloadItem == nil)
{
downloadItem = [self getFreeSlot:YES];
}
[self increaseDownloadCounter];
[downloadItem downloadFile:fileID fileName:name callBack:self];
}
return downloadItem != nil;
}
/**
Find if job is already in progress
**/
-(BOOL)findJobInQueue:(NSInteger)job
{
BOOL error = NO;
FileDownloadControllerItem * downloadItem;
for (int i = 0; i < [downloadArray count]; ++i)
{
downloadItem = (FileDownloadControllerItem *)[downloadArray objectAtIndex:i];
if (downloadItem.jobID == job)
{
error = YES;
}
}
return error;
}
/**
Cancel docwnload given its fileID
**/
-(void)cancelDownloadFile:(NSInteger)fileID
{
[self cancelDownload:[self getSlot:fileID]];
}
/**
Call to cancel download activities
**/
-(void)cancelDownload:(FileDownloadControllerItem *)downloadItem
{
if (downloadItem != nil)
{
[self deleteSession:downloadItem.jobID];
[self decreaseDownloadCounter];
[downloadItem.downloadObject stopDownload];
downloadItem.jobID = FREESLOT;
}
}
/**
Stop downloads
**/
-(void)stopAllDownloads
{
for (int i = 0; i < [downloadArray count]; ++i)
{
FileDownloadControllerItem * downloadItem = (FileDownloadControllerItem *)[downloadArray objectAtIndex:i];
[self deleteSession:downloadItem.jobID];
[self decreaseDownloadCounter];
[downloadItem stopDownload];
downloadItem.jobID = FREESLOT;
}
}
/**
Get a worker given a jobid
**/
-(FileDownloadControllerItem *)getSlot:(NSInteger)job
{
FileDownloadControllerItem * downloadItem = nil;
for (int i = 0; i < [downloadArray count]; ++i)
{
downloadItem = (FileDownloadControllerItem *)[downloadArray objectAtIndex:i];
if (downloadItem.jobID == job)
{
return downloadItem;
}
}
return downloadItem;
}
/**
Get a free worker
**/
-(FileDownloadControllerItem *)getFreeSlot:(BOOL)force
{
FileDownloadControllerItem * downloadItem = nil;
FileDownloadControllerItem * downloadItemReturn = nil;
NSDate * minDate = [NSDate dateWithTimeIntervalSinceNow:0];
for (int i = 0; i < [downloadArray count]; ++i)
{
downloadItem = (FileDownloadControllerItem *)[downloadArray objectAtIndex:i];
if ( ( downloadItemReturn == nil && downloadItem.jobID == FREESLOT ) || force)
{
if (force)
{
if ([downloadItem.startTime timeIntervalSince1970] < [minDate timeIntervalSince1970])
{
minDate = [NSDate dateWithTimeIntervalSince1970:[downloadItem.startTime timeIntervalSince1970]];
downloadItemReturn = downloadItem;
}
}
else
{
downloadItemReturn = downloadItem;
}
}
}
if (downloadItem != nil)
{
[self cancelDownload:downloadItemReturn];
}
return downloadItemReturn;
}
- (void) downloadError:(NSError *)error downloadID:(NSInteger)jobID;
- (void) downloadSucceed:(NSMutableData *)data downloadID:(NSInteger)jobID;
downloadController = [FileDownloadController getController];
[downloadController downloadFile:fileID fileName:url callBack:self];
#pragma mark -
#pragma mark FileDownloadProtocol
/**
Notify an error
**/
- (void) downloadError:(NSError *)error downloadID:(NSInteger)jobID
{
// Notify error to screen
}
/**
Notify download complete
**/
- (void)downloadSucceed:(NSMutableData *)data downloadID:(NSInteger)jobID
{
// Do something with the data, remember the jobID is the ID sent in downloadFile
}
Comments