Alan Alan - 4 months ago 27
iOS Question

How do I create a global UIManagedDocument instance per document-on-disk shared by my whole application using blocks?

I am trying to design a helper method which will retrieve a UIManagedDocument, then open and return it, so that I may access the same UIManagedDocument from several places in my app.

I am having trouble with the asynchronous nature of this as I am not too familiar with blocks. Ideally the sequence of events would be this:


  1. Class X calls the helper method to retrieve the UIManagedDocument and includes a block of code to run when the opened document is returned.

  2. The class method retrieves the UIManagedDocument and calls openWithCompletionHandler or saveToURL as necessary, and includes a block of code to run when the opened document is returned.

  3. openwithCompletionHandler or saveToURL complete their task, and return with success = YES and run the code in its block

  4. The class method completes its task and returns with an open UIManagedDocument and runs the code in its block



Can I pass the original block through somehow?

Here's my code so far. Any thoughts hugely appreciated, thanks.

// This is a dictionary where the keys are "Vacations" and the objects are URLs to UIManagedDocuments
static NSMutableDictionary *managedDocumentDictionary = nil;

// This typedef has been defined in .h file:
// typedef void (^completion_block_t)(UIManagedDocument *vacation);
// The idea is that this class method will run the block when its UIManagedObject has opened

@implementation MyVacationsHelper

+ (void)openVacation:(NSString *)vacationName usingBlock:(completion_block_t)completionBlock
{
// Try to retrieve the relevant UIManagedDocument from managedDocumentDictionary
UIManagedDocument *doc = [managedDocumentDictionary objectForKey:vacationName];

// Get URL for this vacation -> "<Documents Directory>/<vacationName>"
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:vacationName];

// If UIManagedObject was not retrieved, create it
if (!doc) {

// Create UIManagedDocument with this URL
doc = [[UIManagedDocument alloc] initWithFileURL:url];

// Add to managedDocumentDictionary
[managedDocumentDictionary setObject:doc forKey:vacationName];
}

// If document exists on disk...

if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]])
{
[doc openWithCompletionHandler:^(BOOL success)
{
// Can I call the completionBlock from above in here?
// How do I pass back the opened UIDocument
}];

} else {

[doc saveToURL:url
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success)
{
// As per comments above
}];

}

}

Answer

You can execute the block with completionBlock(doc).

    [doc openWithCompletionHandler:^(BOOL success) 
     {
         // Can I call the completionBlock from above in here?
         // How do I pass back the opened UIDocument
        completionBlock(doc);
     }];

Let's assume that you have the following method implemented in the class that will be calling your openVacation method:

-(void)vacationOpened:(UIManagedDocument *)vacation
{
    NSLog(@"My Vacation: %@", vacation.description);
}

A sample line of code that would call your openVacation method would be:

[MyVacationsHelper openVacation:@"MyVacation1" usingBlock:^(UIManagedDocument *vacation){
    [self vacationOpened:vacation];
}];

The (UIManagedDocument *vacation) after the caret means that when you execute the block using the parentheses notation -- as in completionBlock(doc) --, you need to list a (UIManagedDocument *) as a parameter. The value of that parameter will be referred to as vacation inside the specified block. What I did in my block code sample above was call a method in my current class (self) and pass the parameter along to that method so that I could use it as needed (I just did an NSLog here to make sure that it worked).