As I mentioned in my comments, above, the most obvious problem is that you're invoking methods that use condition
before you initialize condition
. Make sure initialize condition
before you start calling updateCompetitionResults
, etc.
In terms of a more radical change, I might suggest retiring NSCondition
altogether, and use operation queues:
I might use
NSOperationQueue
(or you can use dispatch groups, too, if you want, but I like the operation queue's ability to configure how many concurrent operations you can operate ... also if you get to a point that you want to cancel operations, I thinkNSOperationQueue
offers some nice features there, too). You can then define each download and processing as a separateNSOperation
(each of the downloads should happen synchronously, because they're running in an operation queue, you get the benefits of asynchronous operations, but you can kick off the post-processing immediately after the download is done). You then just queue them up to run asynchronously, but define a final operation which is dependent upon the other four will kick off as soon as the four downloads are done. (By the way, I useNSBlockOperation
which provides block-functionality forNSOperation
objects, but you can do it any way you want.)And whereas your
updateProgramContent
might download asynchronously, it processes the four downloaded files sequentially, one after another. Thus, if the first download takes a while to download, it will hold up the post-processing of the others. Instead, I like to encapsulate both the downloading and the post processing of each of the four plist files in a singleNSOperation
, each. Thus, we enjoy maximal concurrency of not only the downloading, but the post-processing, too.Rather than using the
AFNetworking
(which I'm generally a big fan of) plist-related method, I might be inclined to useNSDictionary
andNSArray
features that allow you to download a plist from the web and load them into the appropriate structure. ThesedictionaryWithContentsOfURL
andarrayWithContentsOfURL
run synchronously, but because we're doing this in a background operation, everything runs asynchronously like you want. This also bypasses the saving them to files. If you wanted them saved to files in yourDocuments
directory, you can do that easily, too. Clearly, if you're doing something sophisticated in your downloading of the plist files (e.g. your server is engaging in some challenge-response authentication), you can't use the convenientNSDictionary
andNSArray
methods. But if you don't need all of that, the simpleNSDictionary
andNSArray
methods,___WithContentsOfURL
make life pretty simple.
Pulling this all together, it might look like:
@interface ViewController ()
@property (nonatomic, strong) NSArray *competitions;
@property (nonatomic, strong) NSDictionary *competitionResults;
@property (nonatomic, strong) NSDictionary *competitionRecalls;
@property (nonatomic, strong) NSDictionary *competitionProgress;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self transfer];
}
- (void)allTransfersComplete
{
BOOL success;
if (self.competitions == nil)
{
success = FALSE;
NSLog(@"Unable to download competitions");
}
if (self.competitionResults == nil)
{
success = FALSE;
NSLog(@"Unable to download results");
}
if (self.competitionRecalls == nil)
{
success = FALSE;
NSLog(@"Unable to download recalls");
}
if (self.competitionProgress == nil)
{
success = FALSE;
NSLog(@"Unable to download progress");
}
if (success)
{
NSLog(@"all done successfully");
}
else
{
NSLog(@"one or more failed");
}
}
- (void)transfer
{
NSURL *baseUrl = [NSURL URLWithString:@"http://insert.your.base.url.here/competitions"];
NSURL *competitionsUrl = [baseUrl URLByAppendingPathComponent:@"competitions.plist"];
NSURL *competitionResultsUrl = [baseUrl URLByAppendingPathComponent:@"competitionresults.plist"];
NSURL *competitionRecallsUrl = [baseUrl URLByAppendingPathComponent:@"competitionrecalls.plist"];
NSURL *competitionProgressUrl = [baseUrl URLByAppendingPathComponent:@"competitionprogress.plist"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4; // if your server doesn't like four concurrent requests, you can ratchet this back to whatever you want
// create operation that will be called when we're all done
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// any stuff that can be done in background should be done here
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// any user interface stuff should be done here; I've just put this in a separate method so this method doesn't get too unwieldy
[self allTransfersComplete];
}];
}];
// a variable that we'll use as we create our four download/process operations
NSBlockOperation *operation;
// create competitions operation
operation = [NSBlockOperation blockOperationWithBlock:^{
// download the competitions and load it into the ivar
//
// note, if you *really* want to download this to a file, you can
// do that when the download is done
self.competitions = [NSArray arrayWithContentsOfURL:competitionsUrl];
// if you wanted to do any post-processing of the download
// you could do it here.
NSLog(@"competitions = %@", self.competitions);
}];
[completionOperation addDependency:operation];
// create results operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionResults = [NSDictionary dictionaryWithContentsOfURL:competitionResultsUrl];
NSLog(@"competitionResults = %@", self.competitionResults);
}];
[completionOperation addDependency:operation];
// create recalls operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionRecalls = [NSDictionary dictionaryWithContentsOfURL:competitionRecallsUrl];
NSLog(@"competitionRecalls = %@", self.competitionRecalls);
}];
[completionOperation addDependency:operation];
// create progress operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionProgress = [NSDictionary dictionaryWithContentsOfURL:competitionProgressUrl];
NSLog(@"competitionProgress = %@", self.competitionProgress);
}];
[completionOperation addDependency:operation];
// queue the completion operation (which is dependent upon the other four)
[queue addOperation:completionOperation];
// now queue the four download and processing operations
[queue addOperations:completionOperation.dependencies waitUntilFinished:NO];
}
@end
Now, I don't know which of your plist's are arrays and which are dictionaries (in my example, I made competitions an array and the rest were dictionaries keyed by the competition id), but hopefully you get the idea of what I was shooting for. Maximize concurrency, eliminate NSCondition
logic, really make the most of NSOperationQueue
, etc.
This may be all to much to take in, but I only mention it as an alternative to NSCondition
. If your current technique works, that's great. But the above outlines how I would tackle a challenge like this.