Core data 核心数据&x2B;iCloud场景

Core data 核心数据&x2B;iCloud场景,core-data,ios5,icloud,Core Data,Ios5,Icloud,我有一个很难解决的问题。我有这样一个场景: 我的应用程序使用CoreData存储对象,我想在设备之间实现iCloud同步。。。我的应用程序需要一个初始填充的数据库 我第一次启动我的应用程序时,它将在云端填充我的数据库,并将一些数据库字段标记为“databaseInstalled”。这些字段也在云中同步 现在,当另一台设备首次启动该应用程序时,我希望检索“databaseInstalled”字段以检查是否注入了一些数据,但这是错误的 如果databaseInstalled为false,则注入数据;

我有一个很难解决的问题。我有这样一个场景:

我的应用程序使用CoreData存储对象,我想在设备之间实现iCloud同步。。。我的应用程序需要一个初始填充的数据库

我第一次启动我的应用程序时,它将在云端填充我的数据库,并将一些数据库字段标记为“databaseInstalled”。这些字段也在云中同步

现在,当另一台设备首次启动该应用程序时,我希望检索“databaseInstalled”字段以检查是否注入了一些数据,但这是错误的

如果databaseInstalled为false,则注入数据;如果databaseInstalled为true,则等待iCloud同步

问题是我异步检索persistentStoreCoordinator,因为我不想阻止正在等待从iCloud下载数据的应用程序

那么,如果我需要填充数据库,或者数据库已经在另一台设备上填充,而我只是想从iCloud下载填充的数据库,那么我怎么能事先知道呢

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if((__persistentStoreCoordinator != nil)) {
        return __persistentStoreCoordinator;
    }

    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];    
    NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;

    // Set up iCloud in another thread:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // ** Note: if you adapt this code for your own use, you MUST change this variable:
        NSString *iCloudEnabledAppID = @"this is a secret!";

        // ** Note: if you adapt this code for your own use, you should change this variable:        
        NSString *dataFileName = @"you do not have to know.sqlite";

        // ** Note: For basic usage you shouldn't need to change anything else

        NSString *iCloudDataDirectoryName = @"Data.nosync";
        NSString *iCloudLogsDirectoryName = @"Logs";
        NSFileManager *fileManager = [NSFileManager defaultManager];        
        NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
        NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];

        if (iCloud) {

            NSLog(@"iCloud is working");

            NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];

            NSLog(@"iCloudEnabledAppID = %@",iCloudEnabledAppID);
            NSLog(@"dataFileName = %@", dataFileName); 
            NSLog(@"iCloudDataDirectoryName = %@", iCloudDataDirectoryName);
            NSLog(@"iCloudLogsDirectoryName = %@", iCloudLogsDirectoryName);  
            NSLog(@"iCloud = %@", iCloud);
            NSLog(@"iCloudLogsPath = %@", iCloudLogsPath);

            // da rimuovere

            //[fileManager removeItemAtURL:iCloudLogsPath error:nil];
            #warning to remove

            if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
                NSError *fileSystemError;
                [fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName] 
                       withIntermediateDirectories:YES 
                                        attributes:nil 
                                             error:&fileSystemError];
                if(fileSystemError != nil) {
                    NSLog(@"Error creating database directory %@", fileSystemError);
                }
            }

            NSString *iCloudData = [[[iCloud path] 
                                     stringByAppendingPathComponent:iCloudDataDirectoryName] 
                                    stringByAppendingPathComponent:dataFileName];

            //[fileManager removeItemAtPath:iCloudData error:nil];
#warning to remove

            NSLog(@"iCloudData = %@", iCloudData);

            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
            [options setObject:iCloudEnabledAppID            forKey:NSPersistentStoreUbiquitousContentNameKey];
            [options setObject:iCloudLogsPath                forKey:NSPersistentStoreUbiquitousContentURLKey];

            [psc lock];

            [psc addPersistentStoreWithType:NSSQLiteStoreType 
                              configuration:nil 
                                        URL:[NSURL fileURLWithPath:iCloudData] 
                                    options:options 
                                      error:nil];

            [psc unlock];
        }
        else {
            NSLog(@"iCloud is NOT working - using a local store");
            NSLog(@"Local store: %@", localStore.path);
            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];

            [psc lock];

            [psc addPersistentStoreWithType:NSSQLiteStoreType 
                              configuration:nil 
                                        URL:localStore 
                                    options:options 
                                      error:nil];
            [psc unlock];

        }

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"iCloud routine completed.");

            Setup *install = [[Setup alloc] init];

            if([install shouldMigrate]) {
                HUD = [[MBProgressHUD alloc] initWithView:self.window.rootViewController.view];
                HUD.delegate = self;
                HUD.labelText = NSLocalizedString(@"Sincronizzazione del database", nil);
                [self.window.rootViewController.view addSubview:HUD];
                [HUD showWhileExecuting:@selector(installDatabase) onTarget:install withObject:nil animated:YES];
            }
            else {
                [[NSNotificationCenter defaultCenter] postNotificationName:@"setupCompleted" object:self];
            }

            //[[NSNotificationCenter defaultCenter] postNotificationName:@"icloudCompleted" object:self userInfo:nil];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"setupCompleted" object:self];
        });
    });

    return __persistentStoreCoordinator;
}

在完成与iCloud的同步之前,您无法知道iCloud中是否有可用的数据。这意味着您有两种选择:

  • 让用户等待同步完成

  • 使用默认数据库启动,并尽可能合并iCloud中的更改


使用iCloud,您需要在本地数据和云数据之间进行一些转换,因为您必须处理用户可能同时更改多个设备上的数据这一事实。一旦你做到这一点,很明显上面的第二个选项是更好的:用户可以立即开始使用你的应用程序,云中的数据在可用时被合并。

我也遇到了同样的问题

看看我的问题和答案

事实上,它不能100%正常工作。如果你敢尝试,我可以向你解释如何让它100%正确工作(尽管我还没有尝试过)


考虑到您有大量数据要预填充,我的解决方案现在可能适合您。

无法确定是否首次打开数据存储。至少不在iCloud核心数据存储上。想想看,iCloud也应该离线工作——也就是说,当用户断开与Internet的连接时,所有更改都应该缓冲,然后在恢复连接时上载。如果不可能让用户等待几分钟(如果设备离线,甚至无限期等待)来询问iCloud的数据副本,则无法检查数据存储是否已初始化

要解决此问题,您需要遵循以下四个简单的准则:

  • 有一种消除预填充记录重复的方法
  • 有一种方法可以识别预填充的记录,并将其与用户输入的记录区分开来
  • 每次从iCloud收到新的事务记录时,都要运行重复数据消除过程
  • 每个设备/帐户组合仅记录一次种子数据

您可以在此处阅读更多详细信息:

预填充数据库中的数据量很大,从1k到4k对象(记录)。第二个选择是它现在如何工作,但我有另一个问题。。。该应用程序在第一时间注入对象以填充数据库,同时将数据与iCloud合并。。。我的NSFetchedResultsController中有一个异常(集合被禁用)。。。还是很麻烦…我忘了。。。选项2的主要问题是我的数据库被复制,因为两台设备正在同步iCloud上的本地数据库。。。从iCloud合并本地数据库不会删除现有记录,而是复制其记录!您回答的是基于文档的应用程序,而不是CoreData+iCloud。解决预填充的db场景没有简单的方法。至少我还没找到。主要问题是,一旦用数据预填充数据库,就会有重复的数据。ObjectId将有所不同。很抱歉,我不太喜欢你的解决方案。这几天我想得太多了,也许我找到了更好的办法。我们可以使用预填充的CoreData数据库(使用一些编辑器,如CoreData editor/manager(?),并将整个数据库复制到.nosyc目录中,因为我们需要所有记录都必须具有相同的UUID。.nosync dir的内容不会在云中同步,但会同步该记录上新操作的事务日志。我认为这是最好的办法。最后一个问题是:如果苹果改变预(生成和复制)填充CoreData的数据库会怎么样?我的解决方案确实不是很完美。这正是我在有限的时间内设法解决的问题。您的解决方案是否完全适合您的任务?我预先填写的数据是“类别”,我需要在不同的设备上添加“项目”,以属于同一类别。根据你的经验,这会发生吗?我不太明白你的最后一个问题。你害怕苹果改变核心数据bd格式吗?我3个月前就在开发这个案例,所以我现在记不清每个细节了。但我想我也尝试过你的方法,但我没有得到数据一致性——从iCLoud的角度来看,不同设备上的“类别”确实是不同的实体。如果您报告您的结果,我将不胜感激。是的,我担心苹果可能会更改核心数据数据库文件。我在网上的某个地方读到,苹果在CoreData.hm中存储整数的方式已经从iOS 4改为iOS 5。实际上不知道。但事实上,苹果可以轻易地改变它。那么你的方法完全适合你了吗?