Skip to content

Instantly share code, notes, and snippets.

@ryanjm
Created October 17, 2013 17:52
Show Gist options
  • Save ryanjm/06be2323de367521d8c2 to your computer and use it in GitHub Desktop.
Save ryanjm/06be2323de367521d8c2 to your computer and use it in GitHub Desktop.

Here is a complete run down of the code being used/called.

I'm creating a backgroundQueue in order to push everything off the main thread:

self.backgroundQueue = dispatch_queue_create("com.orangeqc.webservice", NULL);

I'm then creating a bunch of "Commands" to handle downloading a given resource. The first block is a completion block that is called when a given resource finishes all the downloads (since it has multiple pages). In the second section I'm downloading users first, then looping over the rest of my classes and kicking off the download process.

- (IBAction)downloadEverything:(id)sender
{
    self.delegate = nil;
    
    NSArray *classes = @[@"ObjectA", @"ObjectB", ...];
    
    __block int count = 0;
    int downloadCount = [classes count];
    
    void (^compleationBlock)(NSString *) = ^(NSString *className) {
        // go back to the background thread
        dispatch_async(self.backgroundQueue, ^{
            // if the downloads are completed then show alert
            if (count == downloadCount) {
                
                [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait];
                
                // go to the main queue and update UI
                dispatch_sync(dispatch_get_main_queue(), ^{
                    FDStatusBarNotifierView *notifierView = [[FDStatusBarNotifierView alloc] initWithMessage:@"Downloads Complete"];
                    
                    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
                    [notifierView showInWindow:window];
                });
            }
            else if (count % 2 == 0 && count > 0) {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    NSString *msg = [NSString stringWithFormat:@"%i/%i Complete", count, downloadCount];
                    FDStatusBarNotifierView *notifierView = [[FDStatusBarNotifierView alloc] initWithMessage:msg];
                    
                    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
                    [notifierView showInWindow:window];
                });
            }
            DLog(@"%i finished",count);
            count++;
        });
    };
    
    dispatch_async(self.backgroundQueue, ^{
        NSManagedObjectContext *context = [NSManagedObjectContext MR_contextForCurrentThread];
        [context MR_setWorkingName:@"WebServiceBackground"];
                
        __block NSMutableArray *downloads = [NSMutableArray array];
        
        [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            OQCDownloadResourcesCommand *download = [self downloadResource:obj withContext:context];
            [download setCompletionBlock:compleationBlock];
            [downloads addObject:download];
        }];
        
        OQCDownloadResourcesCommand *users = [self downloadResource:@"User" withContext:context];
        [users setCompletionBlock:^(NSString *className) {
            compleationBlock(className);
            
            [downloads enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                [(OQCDownloadResourcesCommand*)obj send];
            }];
        }];
        [users send];
    });
}

Now the two main things inside of OQCDownloadResourcesCommand is the download and process.

- (AFHTTPRequestOperation*)createRequestOperation {
    
    NSDictionary *params = ...;
    
    NSString *path = ...;

    NSMutableURLRequest *request = [self.webService.httpClient requestWithMethod:@"GET"
                                                                            path:path
                                                                      parameters:params];
    
    AFJSONRequestOperation *operation = [AFJSONRequestOperation
         JSONRequestOperationWithRequest:request
         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
         {
             [self saveResources:[JSON objectForKey:@"data"]];
         }
         failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
         {
             DLog(@"%@",error.userInfo);
             [self.webService command:self didFail:error.localizedDescription];
         }];
    
    [operation setQueuePriority:self.priority];
    
    return operation;
}

And then saveResources: looks like this:

- (void)saveResources:(NSArray*)resources {
    BOOL stopDownloads = [self stopDownloadsBasedOnDate:resources];
    // Don't want to stop more downloads
    if ([resources count] > 0 && !stopDownloads){
        self.offset = @([offset intValue] + [resources count]);
        [self send];
    }
    
    // Want to make sure it is fully saved before performing any callbacks
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *blockLocalContext) {
        [self.classRef MR_importFromArray:resources inContext:blockLocalContext];
    } completion:^(BOOL success, NSError *error) {
        if (error){
            DLog(@"error: %@", error);
            [self.webService command:self didFail:[error localizedDescription]];
        }
        else {
            // if the number of resources is 0 or we are stoping downloads
            // then we can perform callbacks
            if ([resources count] == 0 || stopDownloads){
                DLog(@"--finished %@ (%@)",self, className);
                if (performCallbacks) {
                    [self.webService commandDidFinish:self];
                }
                if (completionBlock)
                    completionBlock(className);
            }
        }
    }];
}

The last part of the process is what calls createRequestOperation:

- (void)sendCommand:(OQCCommand*)command {
    if ([self.httpClient networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable){
        DLog(@"Not connected to a network");
    }
    else {
        AFHTTPRequestOperation *operation = [command createRequestOperation];
        [operation setSuccessCallbackQueue:self.backgroundQueue];
        [operation setFailureCallbackQueue:self.backgroundQueue];
        [self.httpClient enqueueHTTPRequestOperation:operation];
        
        DLog(@"Sent command: %@", command);
    }
}

Again, I've tested ot setting the callback queues, but I think I need them to be set.

This is all the main code that is dealing with downloading/importing. This mentions every thread/queue I'm building. Let me know if there is anything else that would be helpful to see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment