Skip to content
zackliston edited this page Dec 30, 2014 · 12 revisions

ZLTaskWorker

The TaskWorker is responsible for the actual work you want to get done. This document will describe how to set up your subclass of TaskWorker to fit your needs.

It may be useful to note that ZLTaskWorker is itself a subclass of NSOperation. If you have worked with NSOperation before you may know about the different flags (isConcurrent, isFinished etc) and other methods for NSOperation. ZLTaskWorker takes care of all these details for you. Do not try to set these flags (expect at the beginning of the start method for concurrent operations, explained later) or call these methods, unexpected behavior will occur.

##Usage Note that any TaskWorker can execute work serially (using only the thread it starts on) or concurrently (using more threads than the one it was started on). ZLTaskWorker requires somewhat different implementation for these different cases. If different implementation is required the docs will specify that, otherwise, treat the implementation of both the same.

Topics Covered

###Initialization ####Serial No special action is required. Simply use the default init method.

####Concurrent You must override the init method. Inside your implementation you must set the isConcurrent flag to YES

- (id)init
{
   self = [super init];
   if (self) {
       self.isConcurrent = YES;
   }
   return self;
}

###Setup The setupWithWorkItem: method should be called for every ZLTaskWorker by it's ZLManager.

It is optional, but recommended for you to override this method. Overriding it allows you to pull out the data and set your own custom variables for easier use later. For example, if your TaskWorker has a integer, string and array given to it, it is easier to set custom variables and assign these values from the given jsonData then to pull out these values from the jsonData every time you need them.

@interface YourTaskWorker () 

@property (nonatomic, assign) NSInteger yourInteger;
@property (nonatomic, strong) NSString *yourString;
@property (nonatomic, strong) NSArray *yourArray;

@end

...
...

- (void)setupWithWorkItem:(ZLInternalWorkItem *)workItem
{
    [super setupWithWorkItem:workItem];
    NSDictionary *jsonData = workItem.jsonData;
    self.yourInteger = [[jsonData objectForKey:kYourIntegerKeyConstant] integerValue];
    self.yourString = [jsonData objectForKey:kYourStringKeyConstant];
    self.yourArray = [jsonData objectForKey:kYourArrayKeyConstant];
}

###Executing Work This is where the actual work is done. The most important thing for you to do is to call taskFinishedWasSuccessful: once all your work is completely finished and only once it is all done, (this is more of an issue for concurrent TaskWorkers). This method must be called at the very end and it must be called only once. This method lets the TaskManager manage the tasks. If it is not called correctly unexpected behavior will occur.

####Serial Serial work must be done in the main method. You may have other method calls inside of the main method but remember that the work must be done serially. So once the end of the main method is reached then all work should be done. Example:

- (void)main 
{
    BOOL success;
    success = [self yourCustomMethod];

    [self taskFinishedWasSuccessful:success];
}

####Concurrent Concurrent work must be done in the start method. Remember, you are responsible for making sure that the taskFinishedWasSuccessful: method is called once at the very end of all work (all the threads you may start are done). If you start other threads, realize that when you reach the end of the start method not all of your work will be done.

In the start method before you do anything else you must set self.isFinished to NO and self.isExecuting to YES

- (void)start
{
   self.isFinished = NO;
   self.isExecuting = YES;

   [self yourCustomAsynchronousMethod];
}

- (void)yourCustomAsychronousMethod
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Do some work here
        BOOL success = // Some work that returns a success flag
        
        // If and only if this was your only asynchronous method OR it is the last one to finish
        [self taskFinishedWasSuccessful:success];
    });
}

For your convienence there is a flag called taskFailed. This flag is set by you and only you. For concurrent tasks it is helpful to set this flag is any operation fails, and then to call the taskFinishedWasSuccessful: method using this flag. For example:

- (void)someAsynchronousMethod
{
   // Do some work here
   BOOL success = // Some work that returns a success flag
   
   if (!success) {
      self.taskFailed = YES;
   }

   if ([self yourMethodForDeterminigIfAllOtherWorkIsDone]) {
        [self taskFinishedWasSuccessful:!self.taskFailed];
    }
}

###Custom taskFinishedWasSuccessful: It is possible for you to override the taskFinishedWasSuccessful method. One use case for this is if you want to send a notification when the task has either succeeded or won't be tried again. Both of which are pieces of information provided to us. We must remember to call [super taskFinishedWasSuccessful:] at then end if we do override this method. One example:

- (void)taskFinishedWasSuccessful:(BOOL)wasSuccessful
{
   if (wasSuccessful || self.isFinalAttempt) {
      NSDictionary *userInfo = @{@"wasSuccessful":[NSNumber numberWithBool:wasSuccessful]];
      [[NSNotificationCenter defaultCenter] postNotificationName:@"aNotificationName" object:nil userInfo:userInfo];
   }
   [super taskFinishedWasSuccessful:wasSuccessful];
}

isFinalAttempt is a flag that is set for us based on how many times this task has already been tried. NOTE even if this task has been set to retry an infinite number of times, this flag may be YES because it the task will only be tried a certain number of times before being reset programmatically or there is a new app launch.

Clone this wiki locally