1
votes

I am concurrently downloading some information from server and I am using NSOperatioQueue for the same. I have an issue. For instance if a download operation fails for some reason I don't want to remove that operation from queue.

Right now even if its a failure as soon as it gets a response back from server the operation is removed from queue.

Is there any way to tell the queue that a particular operation is not logically finished and it should keep it in queue?

In my case, I am downloading a set of information. For example fetching all places in a County and then all houses for each county. So in certain cases county cannot be downloaded if the user is not logged in with a valid token. In that case server returns a failure message. I want to keep such items in queue so that I can try again when user logs in to the app.

Sample Code

self.downloadQueue.maxConcurrentOperationCount = 1;
    for(Campaign *campaign in campaigns)
    {
        isContentUpdated = false;
        if(self.operation)
            self.operation = Nil;
        self.operation = [[DownloadOutlets alloc] initWithCampaign:campaign];
        [self.downloadQueue addOperation:operation];
    }

where downloadQueue is an NSOperationQueue and DownloadOutlets extends NSOperation. Thanks

2
How are your operations set? Show some code examples.Léo Natan
What do you mean? Why would you keep an operation in the queue if it already executed all of the code (whether it was a success or a failure)?micantox
In my case, I am downloading a set of information. For example fetching all places in a County and then all houses for each county. So in certain cases county cannot be downloaded if the user is not logged in with a valid token. In that case server returns a failure message. I want to keep such items in queue.Zach
You probably don't want to keep 'em in the queue; move the failed ones out to a "pendingToken" queue (array) and re-enqueue once the token fault has been addressed.bbum

2 Answers

1
votes

You should not be keeping your failed operations in the queue. The failed operation has performed its task. You should have your operation controller listen to the state of the operations, via completionBlock or otherwise, and decide what to do next. If it comes to the determination that the operation has failed but a similar operation should be retried, it should add another operation to perform the task again.

Another approach would be to retry your download inside the operation until success, and only then end the operation. This is not optimal design, however, because the operation does not, and should not, have all the information required to decide whether to retry, inform the user, etc.

0
votes

You shouldn't keep operations that failed in queue, but use the queue for serial fetching data, and stop queueing if the operation fails :

@implementation DataAdapter
// ...

-(void)setup{
     // weak reference to self to avoid retain cycle
     __weak DataAdapter* selfRef= self;

     // create a block that will run inside the operation queue
     void(^pullCountriesBlock)(void)= ^{
           [[DownloadManager instance] fetchAllCountriesWithCompletionBlock:^(Result* result){
           if(result.successful){
                // on success
                [selfRef didFetchDataForAction:action];
           }else{
                // on failure
                [selfRef failedToFetchDataForAction:action];
           }
     };

     self.actions= [NSMutableArray new];
     [self.actions addObject:[DownloadAction actionWithBlock:pullCountriesBlock];
     // add other actions
     // ...
     [self fetchData];
}

}

-(void)fetchData{
     if(self.currentActionIndex >= self.actions.count){
           [self finishedFetchingData]; 
           return;
     }

     [self fetchDataForAction: self.actions[self.currentActionIndex] ];
}

-(void)fetchDataForAction:(DownloadAction*)action
      [self.myOperationQueueImplementation enqueueOperationWithBlock:action.block];
}

If the download is successful, just enqueue the next action(increment the currentActionIndex and call fetchData). If it fails, you can act accordingly. What I'd do is start listening to interesting NSNotificationCenter events before calling fetchData the first time. You could listen to UserDidLogInNotification or any other that may allow the queue to continue running the downloads.