我正在撰写一款针对iOS7的应用。
我的测试设备是:
1)iPhone 5 16GB
2)iPad第3代16GB Wi-fi + Cellular。
我对UIManagedDocument和iCloud感到生气。
我阅读了我找到的所有文档,我观看了斯坦福CS193P视频,我正在关注Erica Sadun的一些代码。
我无法解决的问题是,在创建本地CoreData文件后,我无法重新打开或保存它,因为它位于[已关闭] SavingError] state。
让我打开并准备就绪非常重要,因为:
1)用户第一次启动应用程序时,它会使用演示数据填充数据库。
2)创建演示数据后,用户可以创建一些新数据,但如果文件处于此状态,则无法保存创建的数据。
3)如果文档处于SavingError状态,则不会保存演示数据。
应用程序中的第一个TableViewController调用fetchDataWithBlock:。块是一个简单的标准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&#34 ;; NSPersistentStoreUbiquitousContentURLKey =" file:/// private / var / mobile / Library / Mobile%20Documents / XXX / CoreDataTransactionsLog /&#34 ;; }
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:[Closed | SavingError]
如您所见,documentState为:[Closed | SavingError]
如果我重新启动应用程序,它会毫无问题地打开数据库,文档状态为NORMAL。 问题是我需要它在第一次运行应用程序时打开并准备就绪并创建演示数据。然后必须使用UIDocumentsaveForOverwriting选项立即保存文件。
我无法要求用户退出应用,然后重新启动或丢失所有的dem数据,因为该文件无法保存!
为了更好地理解发生了什么,我将UIManagedDocument子类化为覆盖handleError:userInteractionPermitted:。
它告诉我文档具有此错误状态,因为"错误域= NSCocoaErrorDomain代码= 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正确重新打开,但每当我尝试使用与创建相同的方法保存它时,它会触发错误代码134030,但值为UIDocumentSaveForOverwriting for forSaveOperation选项。如果用saveToURL覆盖文件,也会发生同样的情况:forSaveOperation:completionHandler:我使用[self.managedDocument.managedObjectContext save:nil]
注1 :我注释掉了所有的演示数据创建代码,以确保它不是应该责怪的。只要空白的UIManagedDocument在创建后成功重新打开,我就会尝试保存它覆盖。
注2 :从UIManagedDocument(本地文件)获取选项DocumentMetaData.plist显示唯一的选项集是NSPersistentStoreUbiquitousContentNameKey。这很奇怪,因为我在创建文件后设置了4个不同的选项。
有关如何解决此问题的任何提示?
由于
尼古拉
答案 0 :(得分:0)
很难理解你的代码在做什么,但是使用UIManagedDocument包装器打开一个新的或现有的核心数据文件似乎要复杂得多。
以下是一件奇怪的事情:
NSPersistentStoreUbiquitousContentNameKey = TransactionsLog (见最后一行)
您不应将此设置为事务日志路径。它应该是一个独特的iCloud商店名称。
此外还不清楚为什么需要一个到事务日志路径的URL - 我从不使用它,也不需要设置事务日志路径,因为Core Data会为您执行此操作。如果您的遗留应用程序使用自定义日志路径,则只需要它。
我会在几分钟内发布一些用于创建UIManagedDocument的代码。
答案 1 :(得分:0)
我想我曾经发过一次,但是又来了。有几点需要注意:
//当用户完成以下操作之一时,将调用此方法: // 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 :(得分:0)
最后我修好了!自从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;
}
}];
}
}