如何控制 iCloud 何时同步我的核心数据?

Posted

技术标签:

【中文标题】如何控制 iCloud 何时同步我的核心数据?【英文标题】:How can I control when iCloud syncs my core data? 【发布时间】:2012-10-23 02:40:28 【问题描述】:

我有以下代码可以将我的核心数据与 iCloud 同步。但是,我只希望它在我说的时候同步。例如,仅在使用 wifi 时同步。我能做到吗?这是我的代码:

- (NSManagedObjectContext *)managedObjectContext 

    if (managedObjectContext != nil) 
        return managedObjectContext;
    

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

    if (coordinator != nil) 
        NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

        [moc performBlockAndWait:^
            [moc setPersistentStoreCoordinator: coordinator];
            [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
        ];
        managedObjectContext = moc;

        managedObjectContext.mergePolicy = [[NSMergePolicy alloc]
                                            initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
    

    return managedObjectContext;


- (void)mergeChangesFrom_iCloud:(NSNotification *)notification 

    NSLog(@"Merging in changes from iCloud...");

    NSManagedObjectContext* moc = [self managedObjectContext];

    [moc performBlock:^

        [moc mergeChangesFromContextDidSaveNotification:notification];

        NSNotification* refreshNotification = [NSNotification notificationWithName:@"SomethingChanged"
                                                                            object:self
                                                                          userInfo:[notification userInfo]];

        [[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
    ];

- (NSManagedObjectModel *)managedObjectModel 

    if (managedObjectModel != nil) 
        return managedObjectModel;
    
    NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"EntryDatabase" ofType:@"momd"];
    NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
    managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    return managedObjectModel;


- (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 = @"iCloud ID is here, i removed it from stack overflow";

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

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

        NSString *iCloudDataDirectoryName = @"Data.nosync";
        NSString *iCloudLogsDirectoryName = @"Logs";
        NSFileManager *fileManager = [NSFileManager defaultManager];

        NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:dataFileName];

        NSURL *localStore = [NSURL fileURLWithPath:storePath];

        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);

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

            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");
            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(), ^
            [[NSNotificationCenter defaultCenter] postNotificationName:@"SomethingChanged" object:self userInfo:nil];
        );
    );

    return persistentStoreCoordinator;

 

【问题讨论】:

Ensembles (ensembles.io) 是我为同步核心数据而开发的框架。它使您可以控制何时同步,并且还会自动迁移和合并现有数据。 【参考方案1】:

据我所知,这不可能发生。无论如何都不是以任何简单有效的方式。

http://developer.apple.com/library/mac/#documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html

问题是iCloud的操作不是透明的。因此,ios 决定何时进行同步以及多久进行一次同步。在上面的文档中,Apple 说“最佳做法”是专门使用沙盒或专门使用 iCloud,但不要在它们之间复制您的数据。

也就是说,Apple 提到了在 iCloud 不可用时进行操作。这似乎符合您建议的情况。从根本上说,您需要正常设置它,确保您拥有所需的数据,然后停用 iCloud 并将您的数据移动到沙箱以进行处理。当您要同步时,只需将数据复制回来即可。

要处理 iCloud 可用性的变化,请实现一个在接收到 NSUbiquityIdentityDidChangeNotification 通知时调用的方法。您的方法需要执行以下工作:

调用 ubiquityIdentityToken 方法并存储其返回值。 将新值与之前的值进行比较,以了解用户是退出其帐户还是登录到其他帐户。 如果以前使用的帐户现在不可用,请根据需要在本地保存当前状态,清空与 iCloud 相关的数据缓存,并刷新所有与 iCloud 相关的用户界面元素。 如果您希望允许用户在 iCloud 不可用的情况下继续创建内容,请将这些内容存储在您应用的沙盒容器中。当帐户再次可用时,将新内容移至 iCloud。通常最好在不通知用户或要求用户进行任何交互的情况下执行此操作。

这似乎符合您的要求。不过,我自己也才刚到这个阶段,所以我不能进一步评论。

【讨论】:

以上是关于如何控制 iCloud 何时同步我的核心数据?的主要内容,如果未能解决你的问题,请参考以下文章

我如何在 iCloud 中同步核心数据?(OS X Lion 应用程序)

使用 iCloud 进行核心数据同步

如何阻止核心数据同步到 iCloud

将 sqlite 数据库与 iCloud 同步

将核心数据与 iCloud 同步 - 不包括实体

是否有可能推断上一次本地核心数据存储完全同步到 iCloud 的时间?