【问题标题】:UIManagedDocument and iCloud: "The provided ubiquity name is already in use."UIManagedDocument 和 iCloud:“提供的通用名称已在使用中。”
【发布时间】:2013-11-30 01:43:22
【问题描述】:

我正在编写一个针对 iOS7 的应用程序。

我的测试设备是:

1) iPhone 5 16GB

2) iPad 第三代 16GB Wi-fi+Cellular。

我对 UIManagedDocument 和 iCloud 感到很生气。

我阅读了我找到的所有文档,观看了斯坦福 CS193P 视频,并且正在关注 Erica Sadun 的一些代码。

我无法解决的问题是,在创建本地 CoreData 文件后,我无法重新打开或保存它,因为它在 [Closed | SavingError] 状态。

打开并准备好它对我来说非常重要,因为:

1) 用户第一次启动应用程序时,它会用演示数据填充数据库。

2) 演示数据创建后,用户可以创建一些新数据,但如果文件处于此状态,则无法保存创建的数据。

3) 如果文档处于 SavingError 状态,演示数据将不会被保存。

应用程序中的第一个 TableViewController 调用 fetchDataWithBlock:。block 是一个简单的标准 NSFetchRequest。

这是我用来为 UIManagedDocument 设置 PSC 选项的代码,并按照 Erica 的建议创建和打开它(创建、关闭、重新打开)。里面有很多登录信息,以便更好地了解发生了什么。

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

        }];

    }
}

这些是日志

2013-11-16 18:19:18.731 当前 iCloud 令牌:

2013-11-16 18:19:19.001 将 iCLoud 与 PSC 选项一起使用:{ NSInferMappingModelAutomaticallyOption = 1; NSMigratePersistentStoresAutomaticallyOption = 1; NSPersistentStoreUbiquitousContentNameKey = "TransactionsLog"; NSPersistentStoreUbiquitousContentURLKey = "file:///private/var/mobile/Library/Mobile%20Documents/XXX/CoreDataTransactionsLog/"; }

2013-11-16 18:19:19.003 应用沙箱中的 CoreData 本地文件不存在。

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 使用本地存储:1

2013-11-16 18:19:19.771 成功:我在应用程序沙箱中创建了 CoreData 本地文件。现在我要关闭它了。

2013-11-16 18:19:19.778 成功:我关闭了刚刚创建的 CoreData 本地文件。现在我重新打开它。

2013-11-16 18:19:20.073 错误:无法重新打开刚刚创建然后关闭的 CoreData 本地文件。无法获取数据。

2013-11-16 18:19:20.074 fileURL: file:///var/mobile/Applications/07702036-765D-414C-9E8B-D4C2B4055CB8/Documents/CoreDataLocalFile documentState: [关闭 |保存错误]

如您所见,documentState 是:[Closed |保存错误]

如果我重新启动应用程序,它会毫无问题地打开数据库,文档状态为正常。 问题是我需要在第一次运行应用程序并创建演示数据时打开并准备好它。然后必须立即使用 UIDocumentsaveForOverwriting 选项保存文件。

我不能要求用户退出应用程序然后重新启动它或丢失所有 dem 数据,因为文件不会被保存!

为了更好地理解发生了什么,我将 UIManagedDocument 子类化以覆盖 handleError:userInteractionPermitted:.

它告诉我文档有这个错误状态,因为“Error Domain=NSCocoaErrorDomain Code=134311 “提供的通用名称已在使用中。”。

2013-11-17 18:33:50.214 [370:1803] UIManagedDocument 中的错误:错误域 = NSCocoaErrorDomain 代码 = 134311“提供的通用名称已在使用中。” UserInfo=0x14edadf0 {NSLocalizedDescription=提供的通用名称已在使用中。,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}

其他信息

如果我搬家:

//设置 iCloud 选项

[self setPersistentStoreOptionsInDocument:_managedDocument];

使用方法 saveToURL:forSaveOperation:completionHandler: 从之前到之后创建本地文件,然后 UIManagedDocument 正确重新打开,但每当我尝试使用用于创建但值 UIDocumentSaveForOverwriting 的相同方法保存它时,它都会触发错误代码 134030对于 forSaveOperation 选项。如果用 saveToURL:forSaveOperation:completionHandler: 我使用 [self.managedDocument.managedObjectContext save:nil] 覆盖文件,也会发生同样的情况

注 1:我注释掉了所有的演示数据创建代码,以确保它不是罪魁祸首。一旦空白的 UIManagedDocument 在创建后成功重新打开,我就会尝试将其保存覆盖。

注 2:从 UIManagedDocument(本地文件)DocumentMetaData.plist 中获取选项表明唯一的选项集是 NSPersistentStoreUbiquitousContentNameKey。这很奇怪,因为我在创建文件后设置了 4 个不同的选项。

关于如何解决这个问题的任何提示?

谢谢

尼古拉

【问题讨论】:

  • 过多的代码转储。请综合。
  • 很难理解这里发生了什么。但是,如果文档在重新启动后正常打开,那么我会怀疑保存/关闭/打开是异步发生的,并且重新打开正在踩着保存/关闭。有没有可以使用的回调或等待?
  • UIManagedDocument 的关闭和重新打开都是通过标准回调异步处理的。我相信真正的问题可能隐藏在 PSC 选项中。

标签: objective-c core-data ios7 icloud uimanageddocument


【解决方案1】:

我想我以前曾发过一次,但又来了。需要注意的几点:

  1. 此应用允许用户创建多个文档,因此需要 UUID 来确保每个 iCloud 文档名称的唯一性。
  2. 打开文件有两种情况,一种是用户在设备上启动文件,另一种是元数据扫描在 iCloud 中找到一个本地不存在的新文件。
  3. 我没有在 iCloud 容器中创建任何东西,一切都由 Core Data 为我设置。我必须使用元数据扫描将 iCloud 中的文件与本地目录中的文件进行比较,并在本地存储不存在时触发创建本地存储。
  4. 只有当它是由用户创建的新文件时,我才会将初始设置数据添加到存储中,在其他情况下,此设置数据将已经由创建该文件的第一个实例的设备创建
  5. 在从 iCloud 导入此初始数据之前,用户无法在应用程序中执行任何操作,但通常这不是问题,因为这只会是从 iCloud 同步的文件的问题。
  6. 在某些情况下,用户可能会导致事情出错,因此需要提供许多文件管理选项,包括从备份中恢复(我的文档存储在 /Documents 中,应该备份)或重建来自 iCloud。不适合胆小的人!
  7. 目前不在生产应用程序中,正在测试中。我们有一个 Mac 版本的应用程序,它具有复杂的文件管理功能,并且不建议使用 iPad 或 iPhone 来管理文件。

// 当用户执行以下操作之一时调用它: // 1. 创建一个新文件并输入一个新的文件名。然后我们创建了 fileURL // 使用 /Documents 目录、文件名并附加 'UUID'+uuid 以确保 // 避免重复文件名,以防用户在另一台设备上使用相同的文件名。 // 2. 从文件浏览器中选择一个现有文件 // - (void)createNewFile:(NSURL*)fileURL {

//FLOG(@"createNewFile called with url %@", fileURL);

_creatingNewFile = YES; // Ignore any file metadata scan events coming in while we do this because some iCloud
                        // files get created by Core Data before the local files are created and our scanning
                        // picks up new iCloud files and attempts to create local copies and we don't want this
                        // if this devices is busy creating the new iCloud file

_document = [[OSManagedDocument alloc] initWithFileURL:fileURL];

// Set oberving on this file to monitor the state (we don't use it for anything other than debugging)
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
           selector:@selector(documentStateChanged:)
               name:UIDocumentStateChangedNotification
             object:_document];

_openedVersion = [NSFileVersion currentVersionOfItemAtURL:fileURL];
_openedVersionDate = _openedVersion.modificationDate;
_openedVersionDevice = _openedVersion.localizedNameOfSavingComputer;
//FLOG(@" file  version date: %@", _openedVersionDate);
//FLOG(@" file  version device: %@", _openedVersionDevice);

NSString *fileName = [[fileURL URLByDeletingPathExtension] lastPathComponent];

[_document setPersistentStoreOptions:@{NSPersistentStoreUbiquitousContentNameKey:fileName,
                                       NSMigratePersistentStoresAutomaticallyOption:@YES,
                                       NSInferMappingModelAutomaticallyOption:@YES,
                                       NSSQLitePragmasOption:@{ @"journal_mode" : @"DELETE" }}];

_managedObjectContext = _document.managedObjectContext;

if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
    //FLOG(@" file exists so open it: %@",fileURL);
    [_document openWithCompletionHandler:^(BOOL success){
        if (!success) {
            // Handle the error.
            LOG(@" error creating file");
        } else {
            //LOG(@" file opened");
            [self fileOpened];  // Now initialise the UI and let the user continue...
        }
    }];

}
else {
    // File does not exist so that means the user has created a new one and we need to
    // load some initialisation data into the Core Data store (codes tables, etc.)
    //
    // At this stage we have a database in memory so we can just use the _document.managedObjectContext
    // to add objects prior to attempting to write to disk.

    //LOG(@" file DOES NOT exist so add initial data");
    [self addInitialData];

    // Just checking if anything has been written to disk, nothing should not exist on disk yet.
    // Debugging use only
    if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
        LOG(@" file exists but keep going anyway :-(");
    else
        LOG(@" file still does not exist :-)");


    // OK now save a copy to disk using UIManagedDocument
    // NOTE: the iCloud files are written before the UIManagedDocument.fileURL, presumably because Core Data does this setup
    // in response to the [moc save:].  Make sure we don't pick this up in our iCloud metaData scan and attempt to create
    // it as if it were a new iCloud file created by some other device.
    //
    [_document saveToURL:_document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success){
        if (!success) {
            // Handle the error.
            LOG(@" error saving file :-(");
        }

        // We close the file and wait for it to appear in the file browser and then
        // let the user select it from the browser to open it and start using it.
        // Skip this if you want to open it directly and [self fileopened] but
        // bear in mind the fileListController will not be correctly set up when we return to it.

        [_document closeWithCompletionHandler:^(BOOL success){
            if (!success) {
                // Handle the error.
                LOG(@" error closing file after creation :-(");
                FLOG(@" file URL is %@", [fileURL path]);
            } else {
                FLOG(@" file closed %@", fileName);

                _creatingNewFile = NO;  // OK we are done, so let metaData scanning go ahead as normal

                // Tell our UITableView file list that we are done and trigger scanning of local and iCloud files
                // The fileListController will the add the new file itself and the user will then pick the
                // file from this list in order to open it.

                // To open the file automatically use the callback in the fileListController
                // to select and then open the file so it looks seamless to the user.
                [self.fileListController fileHasBeenCreated:fileURL];


                // Stop observing now
                [center removeObserver:self
                                  name:UIDocumentStateChangedNotification
                                object:_document];

            }
        }];
    }];
}

}

【讨论】:

    【解决方案2】:

    很难理解您的代码在做什么,但使用 UIManagedDocument 包装器打开一个新的或现有的核心数据文件似乎比必要的要复杂得多。

    一个奇怪的事情是:

    NSPersistentStoreUbiquitousContentNameKey=TransactionsLog(见最后一行)

    您不应将此设置为事务日志路径。它应该是唯一的 iCloud 商店名称。

    还不清楚为什么您需要事务日志路径的 URL - 我从不使用它,也不需要设置事务日志路径,因为 Core Data 会为您执行此操作。仅当您有使用自定义日志路径的旧版应用程序时才需要它。

    我将在几分钟后在此处发布一些用于创建 UIManagedDocument 的代码。

    【讨论】:

      【解决方案3】:

      终于修好了!也许自从 Erica 的演示代码以来,CoreData/UIManagedDocument 内部的某些东西在过去两年中发生了变化。我简化了不使用关闭文档和随后的重新打开例程的数据的创建/打开/获取。现在代码可以工作了。

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

      【讨论】:

        猜你喜欢
        • 2012-04-10
        • 1970-01-01
        • 2012-05-25
        • 1970-01-01
        • 2016-11-19
        • 1970-01-01
        • 2016-09-18
        • 2013-04-08
        • 2013-03-04
        相关资源
        最近更新 更多