Search code examples
objective-ccore-dataios7iclouduimanageddocument

UIManagedDocument and iCloud: "The provided ubiquity name is already in use."


I am writing an app targeting iOS7.

My testing devices are:

1) an iPhone 5 16GB

2) an iPad 3rd generation 16GB Wi-fi+Cellular.

I am getting mad with UIManagedDocument and iCloud.

I read all the documentation I found, I watched the Stanford CS193P videos and I am following some code by Erica Sadun.

The problem I am not able to solve is that after I create the local CoreData file I can't reopen it or save it because it is in [Closed | SavingError] state.

It is very important to me to have it open and ready because:

1) The first time the user launches the app, it fills the DB with demo data.

2) After the demo data creation the user can create some new data but if the file is in this state it can't save the data created.

3) If the document is in SavingError state the demo data won't be saved.

The first TableViewController in the app calls fetchDataWithBlock:.The block is a simply, standard NSFetchRequest.

This is the code I'm using to set the PSC options for the UIManagedDocument, and to create and open it as suggested by Erica (create, close, reopen). There is a lot of logging in it, to better understand what's going on.

#define DB_LOCAL_FILE_NAME @"CoreDataLocalFile"
#define DB_TRANSACTIONS_LOG_FILE_NAME @"TransactionsLog"


-(NSURL *) dbLocalDirectory
{
    //Returns the application's document directory
    _dbLocalDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                                                inDomains:NSUserDomainMask] lastObject];
    return  _dbLocalDirectory;
}


-(NSURL*) iCloudURL
{
    _iCloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    return  _iCloudURL;
}

-(NSURL*) iCloudDataLogFilesURL
{
    _iCloudDataLogFilesURL = [self.iCloudURL URLByAppendingPathComponent:@"CoreDataTransactionsLog"];

    //If the folder is not present, create it.
    if(![[NSFileManager defaultManager] fileExistsAtPath:_iCloudDataLogFilesURL.path]){

        NSLog(@"Creating the iCloudDataLogFilesURL: %@", _iCloudDataLogFilesURL);

        NSError *error;
        if(![[NSFileManager defaultManager] createDirectoryAtPath:_iCloudDataLogFilesURL.path withIntermediateDirectories:YES attributes:nil error:&error]){
            NSLog(@"ERROR creating iCloud folder: %@", error.localizedFailureReason);
        }
    }

    return _iCloudDataLogFilesURL;
}

-(UIManagedDocument *) managedDocument
{
    //Returns the database ManagedDocument

    if (!_managedDocument){

        //Init the document
        _managedDocument = [[UIManagedDocument alloc] initWithFileURL:
            [self.dbLocalDirectory URLByAppendingPathComponent:DB_LOCAL_FILE_NAME]];

    }
    return _managedDocument;
}


-(void) setPersistentStoreOptionsInDocument: (UIManagedDocument*) document
{

    if(self.iCloudDataLogFilesURL){

        NSMutableDictionary *options = [NSMutableDictionary dictionary];

        [options setObject:DB_TRANSACTIONS_LOG_FILE_NAME
                    forKey:NSPersistentStoreUbiquitousContentNameKey];

        [options setObject:self.iCloudDataLogFilesURL
                    forKey:NSPersistentStoreUbiquitousContentURLKey];

        [options setObject:[NSNumber numberWithBool:YES]
                    forKey:NSMigratePersistentStoresAutomaticallyOption];

        [options setObject:[NSNumber numberWithBool:YES]
                    forKey:NSInferMappingModelAutomaticallyOption];

        document.persistentStoreOptions = options;

        //if file exists, use contents of document.fileURL/@"DocumentsMetadata.plist" instead

        NSLog(@"Using iCLoud with PSC Options: %@", document.persistentStoreOptions);
    }else{
        NSLog(@"ERROR. Can't add iCloud options because I don't have a valid iCloudDataLogFilesURL.");
    }
}

-(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. 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(@"The CoreData local file in the application sandbox did not exist.");
        //1. Create the Core Data local File

            //----> Set the iCloud options
        [self setPersistentStoreOptionsInDocument:_managedDocument];


        [self.managedDocument saveToURL:self.managedDocument.fileURL
                       forSaveOperation:UIDocumentSaveForCreating
                      completionHandler:^(BOOL success) {

            if(success){
                //2. Close the Core Data local File
                NSLog(@"SUCCESS: I created the CoreData local file in the application sandbox. Now I am closing it.");
                [self.managedDocument closeWithCompletionHandler:^(BOOL success) {

                    if(success){
                        //3. Reopen the Core Data local File
                        NSLog(@"SUCCESS: I closed the CoreData local file just created. Now I am reopening it.");

                        [self.managedDocument openWithCompletionHandler:^(BOOL success) {

                            if(success){
                                NSLog(@"SUCCESS: I reopened the CoreData local file just created. Fetching data.");
                                fetchingDataBlock();
                            }else{
                                NSLog(@"ERROR: Can't reopen the CoreData local file just created and then closed. Can't fetch the data.");
                                NSLog(@"%@", self.managedDocument);
                                return;
                            }
                        }];

                    }else{
                        NSLog(@"ERROR: Can't close the CoreData local file just created. Can't fetch the data.");
                        NSLog(@"%@", self.managedDocument);
                        return;
                    }

                }];

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

        }];

    }
}

These are the logs:

2013-11-16 18:19:18.731 Current iCloud Token:

2013-11-16 18:19:19.001 Using iCLoud with PSC Options: { NSInferMappingModelAutomaticallyOption = 1; NSMigratePersistentStoresAutomaticallyOption = 1; NSPersistentStoreUbiquitousContentNameKey = "TransactionsLog"; NSPersistentStoreUbiquitousContentURLKey = "file:///private/var/mobile/Library/Mobile%20Documents/XXX/CoreDataTransactionsLog/"; }

2013-11-16 18:19:19.003 The CoreData local file in the application sandbox did not exist.

2013-11-16 18:19:19.032 ApplicationDidBecomeActive

2013-11-16 18:19:19.313 -PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:: CoreData: Ubiquity: mobile~D5AEDBB6-EEFC-455C-A52C-91ADDC1BE081:TransactionsLog Using local storage: 1

2013-11-16 18:19:19.771 SUCCESS: I created the CoreData local file in the application sandbox. Now I am closing it.

2013-11-16 18:19:19.778 SUCCESS: I closed the CoreData local file just created. Now I am reopening it.

2013-11-16 18:19:20.073 ERROR: Can't reopen the CoreData local file just created and then closed. Can't fetch the data.

2013-11-16 18:19:20.074 fileURL: file:///var/mobile/Applications/07702036-765D-414C-9E8B-D4C2B4055CB8/Documents/CoreDataLocalFile documentState: [Closed | SavingError]

As you can see the documentState is: [Closed | SavingError]

If I restart the app, it opens the DB without problems, the document state is NORMAL. The problem is that I need it open and ready the first time I run the app and I create the demo data. Then the file has to be immediately saved witht the UIDocumentsaveForOverwriting option.

I can't ask the user to quit the app and then restart it or losing all the dem data because the file won't be saved!

To better understand what's going on I subclassed UIManagedDocument to override handleError:userInteractionPermitted:.

It tells me the document has this error state because "Error Domain=NSCocoaErrorDomain Code=134311 "The provided ubiquity name is already in use.".

2013-11-17 18:33:50.214 [370:1803] ERROR in UIManagedDocument: Error Domain=NSCocoaErrorDomain Code=134311 "The provided ubiquity name is already in use." UserInfo=0x14edadf0 {NSLocalizedDescription=The provided ubiquity name is already in use., NSURL=file:///var/mobile/Applications/A0B371E0-C992-4D57-895A-E2CCB8A35367/Documents/CoreDataLocalFile/StoreContent.nosync/CoreDataUbiquitySupport/mobile~0EDD3A67-63F4-439F-A055-A13808949BBD/TransactionsLog/A9728F87-0F79-4FE3-9B76-AABD3950BB67/store/persistentStore, NSPersistentStoreUbiquitousContentNameKey=TransactionsLog}

Additional Infos:

if I move:

//Set the iCloud options

[self setPersistentStoreOptionsInDocument:_managedDocument];

from before to after the local file creation with the method saveToURL:forSaveOperation:completionHandler: then the UIManagedDocument reopens correctly but it triggers an error code 134030 whenever I try to save it using the same method used for creation but with the value UIDocumentSaveForOverwriting for the forSaveOperation option. The same happens also if instead to overwrite the file with saveToURL:forSaveOperation:completionHandler: I use [self.managedDocument.managedObjectContext save:nil]

Note 1: I commented out all the demo data creation code to be sure it was not the one to blame. As soon as the blank UIManagedDocument is reopened succesfully after its creation I try to save it overwriting.

Note 2: Getting the options from the UIManagedDocument (local file) DocumentMetaData.plist shows that the only option set is NSPersistentStoreUbiquitousContentNameKey. This is weird because I set 4 different options after the file creation.

Any hints on how to fix this?

Thanks

Nicola


Solution

  • Finally I fixed it! Maybe something internal in CoreData/UIManagedDocument has changed in the last two years, since Erica's demo code. I simplified the creation/opening/fetching of data not using the close document and subsequent reopening routines. Now the code works.

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