使用 iCloud 时使用 NSFetchedResultsController 的表开始为空

Posted

技术标签:

【中文标题】使用 iCloud 时使用 NSFetchedResultsController 的表开始为空【英文标题】:Table using NSFetchedResultsController starts empty when using iCloud 【发布时间】:2012-10-29 18:03:56 【问题描述】:

我的应用程序设置为使用 iCloud 的核心数据,但是当它启动时,显示数据的 UITableView 是空的,需要一点时间来填充数据。有没有办法让它立即显示数据,就好像它没有集成 iCloud 一样?

- (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 = @"IDRemovedFrom***";

        // ** 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;

 

- (NSFetchedResultsController *)fetchedResultsController 

    if (_fetchedResultsController != nil) 
        return _fetchedResultsController;
    

    //Set up the fetched results controller.

    // Create the fetch request for the entity.

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Entry" inManagedObjectContext:[AppDelegate applicationDelegate].managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Sort using the timeStamp property..
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Use the sectionIdentifier property to group into sections.
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[AppDelegate applicationDelegate].managedObjectContext sectionNameKeyPath:@"sectionIdentifier" cacheName:@"Root"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    self.fetchedResultsController.delegate = self;

    return _fetchedResultsController;

【问题讨论】:

【参考方案1】:

一种可能的解决方案是为您的包创建一个预填充的 .sqlite 文件,然后在创建持久存储协调器之前将其复制到 Documents 目录

【讨论】:

以上是关于使用 iCloud 时使用 NSFetchedResultsController 的表开始为空的主要内容,如果未能解决你的问题,请参考以下文章

禁用 iCloud 时是不是可以使用 NSMetadataQuery?

当用户使用 3G 时如何停止 iCloud 同步和备份

使用 Core Data 和 iCloud 添加持久存储时涉及 PFUbiquityPeer 的异常

使用 CloudKit/iCloud 时 Apple Store 构建被拒绝

使用 setUbiquitous 为 UIDocument 文件关闭 iCloud 同步时出错(LibrarianErrorDomain 错误 2)

iCloud 钥匙串在更新时通知