When loading a document from iCloud, one must make a call to
openWithCompletionHandler:^(BOOL success)completionHandler
This function will start a background thread that download the file from iCloud, and upon finishing the load, runs the completion handler.
While that’s happening, any code after this call continues running.
How can I stop the program from continuing to run until after the download is complete?
Immediately after making a call to load from the cloud, my code tries to use the document – but of course, since it isn’t finished downloading, the code crashes.
Details follow:
I’m currently working on a save-file library to handle saving/loading of files, encryption, compression, and iCloud support. Users make a call to my library function LoadFileAtPath and there is a parameter specifying if they want me to check iCloud for this particular file.
When the function starts and I am meant to check on iCloud, the first thing I do is make a call to my iCloudload function, like so:
bool cloudLoad = [iCloudStorageManager readFileFromiCloud:filePath name:fileName];
within this function I make the call to openWithCompletionHandler, like so:
if ([fileManager fileExistsAtPath:[docURL path]])
{
[doc openWithCompletionHandler:^(BOOL success){
if (!success)
{
// Handle the error.
NSLog(@"Failed to retrieve document:%@ from iCloud URL:%@.", fileName, [docURL absoluteString]);
}
else
{
NSLog(@"Replacing document in sandbox with iCloud version");
BOOL* dir = nil;
if (![fileManager fileExistsAtPath:filePath isDirectory:dir])
{
NSError *fileError = nil;
if(![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&fileError])
{
NSLog(@"Error creating file directory: %@\n%@\n%@\n%@", [fileError localizedDescription], [fileError localizedFailureReason], [fileError localizedRecoveryOptions], [fileError localizedRecoverySuggestion]);
}
}
// save new data if success
[iCloudStorage replaceDocumentInSandboxWith:doc newData:doc.loadedContents];
}
}];
[doc release];
}
After that, my code continues by trying to pull up the data, and do various things with it such as decrypt, decompress, and hand the file data to the caller.
My current (hacky) solution was to have, after the initial call to readFileFromiCloud, an loop that checks a bool, like so:
while (!cloudDidFinishLoad)
{
sleep(10);
}
This bool is initialized to false, and within the function seen above “replaceDocumentInSandboxWith”, the bool is set to true.
However, I have found that this causes the program to hang, it never finishes loading the iCloud file, and the completion handler never gets called.
I’m at a loss as to what I should do. I need the program to stop running code until the download is finished, but I don’t know how to do this.
Not sure if you found an answer to this or not, but what I would do is create a protocol for your library and then set up a delegate that you can make a call to when the completion handler finishes.
Then in your main controller can be set up as the delegate and once the file is ready you can receive the call from the library that the file is ready to use. So the pseudo code would be something like this…
Do whatever you need to do here, set up activity indicator or lock out the interface or neither and just assume it worked unless the delegate tells you it didn’t. Basically here your block will end and when the delegate function is called your main controller will pick back up control of the interface.
In the delegate function you will receive the status from the iCloudStorageManager of whether it was successful or not. Once you get that call from the iCloudStorageManager you can free up the interface, post an alert that things went well or that things went south and offer a reply…whatever you want to do.
But that is the way I would handle it. (Yes this is an old question, but I felt like I had some insight :))