11
votes

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
        }];

    }

}
2

2 Answers

7
votes

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).