核心数据与合奏同步:在本地模型更改之前不会下拉远程更改

时间:2017-12-17 21:51:10

标签: ios objective-c icloud ensembles

我有一个Core Data应用程序,我正在尝试使用iCloud作为我的后端集成Ensembles框架。我有大部分工作,除了在一台设备上进行更改时,我必须进行更改并将上下文保存在另一台设备上,以便获取远程更改。

反映数据的tableview符合NSFetchedResultsControllerDelegate。当本地数据更改并且它接收远程更改时,远程更改会正确反映。

实施“同步”按钮,手动调用syncWithCompletion(下方)不会获取更改。

每两分钟发出一次计时器,呼叫syncWithCompletion,但不会接收更改。

关闭然后再次打开同步确实会接收更改。

重新启动应用程序无法获取更改。

#pragma mark - ENSEMBLES

- (void)setupEnsembles {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }

  // set the sync UI on
  [[NSNotificationCenter defaultCenter] postNotificationName:@"setSyncUIOn" object:nil];

  // setup ensemble
  self.cloudFileSystem = [[CDEICloudFileSystem alloc] initWithUbiquityContainerIdentifier:nil];
  self.ensemble = [[CDEPersistentStoreEnsemble alloc] initWithEnsembleIdentifier:@"RecordStore"
                                                              persistentStoreURL:self.storeURL
                                                           managedObjectModelURL:[self modelURL]
                                                                 cloudFileSystem:self.cloudFileSystem];
  self.ensemble.delegate = self;

  // Listen for local saves, and trigger merges
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(localSaveOccurred:)
                                               name:CDEMonitoredManagedObjectContextDidSaveNotification
                                             object:nil];

  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(cloudDataDidDownload:)
                                               name:CDEICloudFileSystemDidDownloadFilesNotification
                                             object:nil];

  [self syncWithCompletion:NULL];

  // configure a timer to trigger a merge every two minutes
  if (!self.ensemblesSyncTimer) {
    self.ensemblesSyncTimer = [NSTimer scheduledTimerWithTimeInterval:120.0
                                                               target:self
                                                             selector:@selector(performScheduledSync:)
                                                             userInfo:nil
                                                              repeats:YES];
  }
}

- (void)performScheduledSync:(NSTimer*)aTimer {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  [self syncWithCompletion:NULL];
}

- (void)syncWithCompletion:(void(^)(void))completion {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }

  // set the sync UI on
  [[NSNotificationCenter defaultCenter] postNotificationName:@"setSyncUIOn" object:nil];

  // this checks to make sure there is an ensemble, because this method
  // can be called without knowing whether ensembles is enabled or not
  if (self.ensemble) {
    if (coreDataDebug==1) { NSLog(@"there is an ensemble, going to leech or merge"); }

    if (!self.ensemble.isLeeched) {
      if (coreDataDebug==1) { NSLog(@"leeching"); }
      [self.ensemble leechPersistentStoreWithCompletion:^(NSError *error) {
        if (error) NSLog(@"Error in leech: %@", [error localizedDescription]);

        // set the last synced date
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.timeStyle = NSDateFormatterMediumStyle;
        dateFormatter.dateStyle = NSDateFormatterMediumStyle;
        [dateFormatter setLocale:[NSLocale currentLocale]];
        [[NSUserDefaults standardUserDefaults] setObject:[dateFormatter stringFromDate:[NSDate date]]
                                                  forKey:@"iCloudLastSyncDate"];

        [[NSNotificationCenter defaultCenter] postNotificationName:@"setSyncUIOff" object:nil];
                                                            object:nil];
        if (completion) {completion();}
      }];
    }
    else {
      if (coreDataDebug==1) { NSLog(@"merging"); }
      [self.ensemble mergeWithCompletion:^(NSError *error) {
        if (error) NSLog(@"Error in merge: %@", [error localizedDescription]);

        // set the last synced date
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.timeStyle = NSDateFormatterMediumStyle;
        dateFormatter.dateStyle = NSDateFormatterMediumStyle;
        [dateFormatter setLocale:[NSLocale currentLocale]];
        [[NSUserDefaults standardUserDefaults] setObject:[dateFormatter stringFromDate:[NSDate date]]
                                                  forKey:@"iCloudLastSyncDate"];

        [[NSNotificationCenter defaultCenter] postNotificationName:@"setSyncUIOff" object:nil];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"recordCollectionNeedsRefresh"
                                                            object:nil];
        if (completion) {completion();}
      }];
    }
  }
}

- (void)localSaveOccurred:(NSNotification *)notif {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  [self syncWithCompletion:NULL];
}

- (void)cloudDataDidDownload:(NSNotification *)notif {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  [self syncWithCompletion:NULL];
}

- (void)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble didSaveMergeChangesWithNotification:(NSNotification *)notification {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }

  [_context performBlockAndWait:^{
    [_context mergeChangesFromContextDidSaveNotification:notification];
  }];
}

- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble globalIdentifiersForManagedObjects:(NSArray *)objects {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  return [objects valueForKeyPath:@"uniqueIdentifier"];
}

- (void)removeEnsembles {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  [self disconnectFromSyncServiceWithCompletion:NULL];
}

- (void)disconnectFromSyncServiceWithCompletion:(CDECodeBlock)completion {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  [self.ensemble deleechPersistentStoreWithCompletion:^(NSError *error) {

    self.ensemble.delegate = nil;
    [self.ensemble dismantle];
    self.ensemble = nil;
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudLastSyncDate"];
    [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"iCloudSyncingEnabled"];
    [self.ensemblesSyncTimer invalidate];
    self.ensemblesSyncTimer = nil;

  if (completion) completion();
  }];
}

- (void)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble didDeleechWithError:(NSError *)error {
  if (ensemblesDataDebug==1) { printf("Running %s\n", [NSStringFromSelector(_cmd) UTF8String]); }
  NSLog(@"Store did deleech with error: %@", error);
}

我出错的任何想法?

[编辑,因为我的评论太长了]

首先,didSaveMergeChangesWithNotification如果我进行本地保存并且云中有变化(假设它们已经传播了 - 有没有办法知道?我已经被调用我等了很长时间试图排除这个问题,当我触发手动同步时也没有被调用。只有在我对本地模型进行更改然后保存上下文时才会调用它。我不太清楚离开我的地方。其次,检查获取控制器,云中的变化确实没有被拉下来。我已经打开CDELoggingLevelVerbose继续调查,但我知道我做的事情根本就是错误的,我必须要失踪。

此外,这是一个重大变化 - 我刚刚从Ensembles Github中的一个旧问题中意识到,在模拟器中触发iCloud同步实际上是有效的!不幸的是,我正在模拟器中进行所有测试,因为我没有任何设备(我在测试过程中烧了我的iPhone太多iCloud登录)。这可能吗?我是否可以确信这实际上正常工作,但模拟器中有些东西实际上并没有让iCloud同步触发?

1 个答案:

答案 0 :(得分:1)

It's not clear to me why it is not working, but there are some things you can try to find out.

First, try to figure out from your logs what is different when you do a local save as opposed to just a merge (by pressing the sync button). Does the didSaveMergeChangesWithNotification: delegate method get triggered in both cases? Assuming there are changes in the cloud, it should.

It's also worth checking the fetch results controller. It is possible the changes do enter the store, but that the fetch controller doesn't pick them up. One way to check is to call performFetch and reload your UI at the end of each merge, just to test if that could be the problem.

Another way to see if Ensembles is actually getting and merging the data is to turn on the verbose logging. Use the function CDESetCurrentLogLevel, and pass in CDELoggingLevelVerbose. That will print a lot of information about what the framework is doing, and should give clues.