3

I am creating a new UIManagedDocument with iCloud support as follows:

  1. Alloc and init with local sandbox URL
  2. Set persistent store options to support iCloud: ubiquitousContentNameKey and ubiquitousContentURL. The name I'm generating uniquely and the URL is pointing to my ubiquityContainer / CoreData.
  3. Save locally to sandbox with UIManagedDocument's saveToURL method.
  4. In completion handler, move to iCloud with FileManager's setUbiquitous method.

So far, this dance works. (Well, sort of). After I call setUbiquitous, I get an error that says it WASN'T successful, however the document moves to the cloud. When it's done, I have a new document in the cloud. This appears to be a bug, as I've been able to replicate it with others' code.

I'm actually generating this document in a "Documents View Controller," which lists all of the documents in the cloud. So when this new document's final completion handler is finished, it shows up in the table view thanks to an NSMetadataQuery. So far, pretty standard usage I think.

To edit a document, the user taps and goes to a "Single View Document View Controller."

In this view controller, I need to "reopen" the selected document so the user can edit it.

So I go through series of steps again:

  1. Alloc / init a UIManagedDocument with a fileURL -- this time, the URL is from the cloud.
  2. Set my persistent store options, same as step 2 above, with same settings.

Now, I ATTEMPT step 3, which is to open the document from disk, but it fails. The document is in a state of "Closed | SavingError" and the attempt to open fails.

Does anyone know why my document would create OK, move to the cloud OK, but then fail to open on an immediate subsequent attempt? (Really, an attempt within that launch of the app - see below). Specifically, what would make a UIManagedDocument instance be created but in a closed, non-openable state?

Interestingly enough, if I quit the app and launch again, I can tap and reload the document and edit it.

And very occasionally I can create, then open, and edit very briefly, say insert one managedobject, and then it goes into this close | saving error state.

ERROR INFO:

I've subclassed UIManagedDocument and overrode the -handleError: method to try and get more information, and here's what I get (along with some other debugging logs I put in):

2012-10-05 14:57:06.000 Foundations[23687:907] Single Document View Controller View Did Load. Document: fileURL: file://localhost/private/var/mobile/Library/Mobile%20Documents/7PB5426XF4~com~howlin~MyApp/Documents/New%20Document%2034/ documentState: [Closed]

2012-10-05 14:57:06.052 MyApp[23687:907] Document state changed. Current state: 5 fileURL: file://localhost/private/var/mobile/Library/Mobile%20Documents/7PB5426XF4~com~howlin~MyApp/Documents/New%20Document%2034/ documentState: [Closed | SavingError]

2012-10-05 14:57:06.057 Foundations[23687:5303] UIManagedDocument error: The store name: New Document 34 is already in use. Store URL: file://localhost/private/var/mobile/Library/Mobile%20Documents/7PB5426XF4~com~howlin~MyApp/Documents/New%20Document%2034/StoreContent.nosync/persistentStore In Use Store URL: file://localhost/var/mobile/Applications/D423F5FF-4B8E-4C3E-B908-11824D70FD34/Documents/New%20Document%2034/StoreContent.nosync/persistentStore

2012-10-05 14:57:06.059 MyApp[23687:5303] { NSLocalizedDescription = "The store name: New Document 34 is already in use.\n\tStore URL: file://localhost/private/var/mobile/Library/Mobile%20Documents/7PB5426XF4~com~howlin~MyApp/Documents/New%20Document%2034/StoreContent.nosync/persistentStore\n\tIn Use Store URL: file://localhost/var/mobile/Applications/D423F5FF-4B8E-4C3E-B908-11824D70FD34/Documents/New%20Document%2034/StoreContent.nosync/persistentStore\n"; NSPersistentStoreUbiquitousContentNameKey = "New Document 34"; }

The error seems to think I'm it create a store that already exists on the subsequent opening. Am I now supposed to set those iCloud option on the persistent store on a second opening? I've tried that approach and it didn't work either.

I've studied the Stanford lectures on UIManagedDocument and don't see what I'm doing wrong.

Here's my method to create the doc and move to cloud:

- (void) testCreatingICloudDocWithName:(NSString*)name
{

    NSURL* cloudURL = [self.docManager.iCloudURL URLByAppendingPathComponent:name isDirectory:YES];

    NSURL* fileURL = [self.docManager.localURL URLByAppendingPathComponent:name];


    self.aWriting = [[FNFoundationDocument alloc] initWithFileURL:fileURL];


    [self setPersistentStoreOptionsInDocument:self.aWriting];

    [self.aWriting saveToURL:fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
        if (success == YES) {

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                //create file coordinator
                //move document to icloud

                NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
                NSError* coorError = nil;
                [fileCoordinator coordinateWritingItemAtURL:cloudURL options:NSFileCoordinatorWritingForReplacing error:&coorError byAccessor:^(NSURL *newURL) {

                    if (coorError) {
                        NSLog(@"Coordinating writer error: %@", coorError);
                    }

                    NSFileManager* fm = [NSFileManager defaultManager];
                    NSError* error = nil;
                    NSLog(@"Before set ubiq");
                    [fm setUbiquitous:YES itemAtURL:fileURL destinationURL:newURL error:&error];
                    if (!error) {
                        NSLog(@"Set ubiquitous successfully.");
                    }
                    else NSLog(@"Error saving to cloud. Error: %@", error);


                    NSLog(@"State of Doc after error saving to cloud: %@", self.aWriting);
                }];



            });

        }
    }];

}

Here's where I set options for iCloud on the persistentStore:

- (void)setPersistentStoreOptionsInDocument:(FNDocument *)theDocument
{
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
    [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];

    [options setObject:[theDocument.fileURL lastPathComponent] forKey:NSPersistentStoreUbiquitousContentNameKey];
    NSURL* coreDataLogDirectory = [self.docManager.coreDataLogsURL URLByAppendingPathComponent:[theDocument.fileURL lastPathComponent]];
    NSLog(@"Core data log dir: %@", coreDataLogDirectory);
    [options setObject:coreDataLogDirectory forKey:NSPersistentStoreUbiquitousContentURLKey];

    theDocument.persistentStoreOptions = options;
}

And here's where I try to reopen it:

- (void) prepareDocForUse
{

    NSURL* fileURL = self.singleDocument.fileURL;

    if (![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
        NSLog(@"File doesn't exist");
            }
    else if (self.singleDocument.documentState == UIDocumentStateClosed) {
        // exists on disk, but we need to open it
        [self.singleDocument openWithCompletionHandler:^(BOOL success) {
            if (!success) {
                NSError* error;
                [self.singleDocument handleError:error userInteractionPermitted:NO];
            }
            [self setupFetchedResultsController];
        }];
    } else if (self.singleDocument.documentState == UIDocumentStateNormal) {
        // already open and ready to use
        [self setupFetchedResultsController];
    }

}
Jason C. Howlin
  • 3,858
  • 3
  • 21
  • 29
  • Is this on the simulator? If yes, which iOS version? Me and others had very strange problems with the iOS 5.1 simulator in Xcode using derivates of the Stanford code – brainray Oct 06 '12 at 17:41
  • This is iOS 6 on a device. Unfortunately you can't test for iCloud on the simulator. – Jason C. Howlin Oct 06 '12 at 22:38
  • I think it's the setUbiquitous method. Regardless of what Apple's docs say, I think it's unnecessary when working with UIManagedDocument. Unless someone can explain the difference between setUbiquitous and saveToURL using a ubiquityContainer URL?? It seems to work OK when I just saveToURL right to iCloud. – Jason C. Howlin Oct 06 '12 at 22:46
  • You might try setting a symbolic breakpoint on setUbiquitous. I haven't gotten to this point yet, but it seems reasonable that the framework may be making the call. – Steven McGrath Oct 07 '12 at 02:51
  • Hello Jason, I am experiencing the very same problem! Have you fixed it? – nico9T Nov 17 '13 at 19:37

2 Answers2

0

Have you recently been testing various version of iOS? Try changing the title of your saved document to something other than "New Document 34", I was experiencing this same issue and I believe it had to do with conflicting documents saved from different sdk compilations of the app using the same document url.

jwilkey
  • 326
  • 3
  • 11
0

I target iOS7, I use a single UIManagedDocument as my app DB with the goal to better integrate CoreData and iCloud as suggested by Apple in its documentation. I had the same problem, i solved with the following code.

Since I wrote it, I moved the PSC options settings inside the lazy instantiation of the UIManagedDocument.

My original code created, closed and then reopened the document using the callback with success standard functions. I found it on a book by Erika Sadun. Everything seemed ok but I couldn't reopen the just created and then closed document because it was in "savings error" state. I lost a week on it, I couldn't understand what I was doing wrong because until the reopen everything was perfect.

The following code works perfectly on my iPhone5 and iPad3.

Nicola

-(void) fetchDataWithBlock: (void (^) (void)) fetchingDataBlock
{

    //If the CoreData local file exists then open it and perform the query
    if([[NSFileManager defaultManager] fileExistsAtPath:[self.managedDocument.fileURL path]]){
        NSLog(@"The CoreData local file in the application sandbox already exists.");

        if (self.managedDocument.documentState == UIDocumentStateNormal){
            NSLog(@"The CoreData local file it's in Normal state. Fetching data.");
            fetchingDataBlock();
        }else if (self.managedDocument.documentState == UIDocumentStateClosed){
            NSLog(@"The CoreData local file it's in Closed state. I am opening it.");

            [self.managedDocument openWithCompletionHandler:^(BOOL success) {
                if(success){
                    NSLog(@"SUCCESS: The CoreData local file has been opened succesfully. Fetching data.");
                    fetchingDataBlock();
                }else{
                    NSLog(@"ERROR: Can't open the CoreData local file. Can't fetch the data.");
                    NSLog(@"%@", self.managedDocument);
                    return;
                }
            }];
        }else{
            NSLog(@"ERROR: The CoreData local file has an unexpected documentState: %@", self.managedDocument);
        }
    }else{
        NSLog(@"The CoreData local file in the application sandbox did not exist.");
             NSLog(@"Setting the UIManagedDocument PSC options.");
        [self setPersistentStoreOptionsInDocument:self.managedDocument];

        //Create the Core Data local File
        [self.managedDocument saveToURL:self.managedDocument.fileURL
                       forSaveOperation:UIDocumentSaveForCreating
                      completionHandler:^(BOOL success) {

            if(success){

                NSLog(@"SUCCESS: The CoreData local file has been created. Fetching data.");
                fetchingDataBlock();

            }else{
                NSLog(@"ERROR: Can't create the CoreData local file in the application sandbox. Can't fetch the data.");
                NSLog(@"%@", self.managedDocument);
                return;
            }

        }];

    }
}
nico9T
  • 2,496
  • 2
  • 26
  • 44