Подтвердить что ты не робот

ICloud Core Обмен данными между OSX и iOS

У меня есть приложение для OSX и iOS, и оба используют iCloud для обмена основными данными между ними. Когда я вношу изменения в iPhone, я вижу их в приложении OSX, а NSPersistentStoreDidImportUbiquitousContentChangesNotification вызывается в обоих приложениях, поэтому iOS → Mac работает хорошо. Но когда я делаю некоторые изменения в приложении Mac, я не вижу их в iPhone. IPhone никогда не называет NSPersistentStoreDidImportUbiquitousContentChangesNotification. Единственный способ, которым я могу получить изменения в Mac, - внести некоторые изменения в iPhone. Затем я вижу все изменения.

Интеграция iCloud в обоих проектах одинакова. Основная база данных данных данных одинаков. Права также одинаковы.

Весь мой код основан на этой библиотеке: http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/

Почему iPhone не получает изменения Mac, пока я не сделаю некоторые изменения на iPhone? Любые идеи?

Другой вопрос

В моем приложении, когда пользователь разрешает iCloud, я проверяю, является ли файл iCloud уже. Если файл существует, пользователь может выбрать восстановление данных ядра из файла iCloud или запуск новой копии из локального хранилища. Чтобы начать новую копию, я использую этот код:

- (void)startNewCopy
{
    [self removeICloudStore];
    [self moveStoreToIcloud];
}

- (void)removeICloudStore
{
    BOOL result;
    NSError *error;
    // Now delete the iCloud content and file
    result = [NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:[self icloudStoreURL]
                                                                             options:[self icloudStoreOptions]
                                                                             error:&error];
    if (!result)
    {
        NSLog(@"Error removing store");
    }
    else
    {
        NSLog(@"Core Data store removed.");
        // Now delete the local file
        [self deleteLocalCopyOfiCloudStore];
    }

}


- (void)deleteLocalCopyOfiCloudStore {
   // We need to get the URL to the store
   NSError *error = nil;
   [[NSFileManager defaultManager] removeItemAtURL:[self localUbiquitySupportURL] error:&error];
}

- (void)moveStoreToIcloud
{
     // Open the store
     NSPersistentStore *sourceStore = [[_persistentStoreCoordinator persistentStores] firstObject];

     if (!sourceStore)
     {
         NSLog(@" failed to add old store");
     }
     else
     {
         NSLog(@" Successfully added store to migrate");
         NSError *error;
         id migrationSuccess = [_persistentStoreCoordinator migratePersistentStore:sourceStore toURL:[self icloudStoreURL] options:[self icloudStoreOptions] withType:NSSQLiteStoreType error:&error];

         if (migrationSuccess)
         {
             NSLog(@"Store migrated to iCloud");

             _persistentStoreCoordinator = nil;
             _managedObjectContext = nil;

             // Now delete the local file
             [self deleteLocalStore];

         }
         else
         {
             NSLog(@"Failed to migrate store: %@, %@", error, error.userInfo);

         }
     }

}

- (void)deleteLocalStore
{
     NSError *error = nil;
     [[NSFileManager defaultManager] removeItemAtURL:[self localStoreURL] error:&error];
}

Выполнение этого с одного устройства, похоже, работает хорошо, но когда я пытаюсь использовать это с несколькими устройствами, я получаю некоторые ошибки.

Пример:

У меня есть два устройства. Первый не подключен к iCloud, а второй подключен к iCloud. В первом устройстве, когда я включаю iCloud, я выбираю startNewCopy, а затем во втором устройстве получаю эту ошибку:

CoreData: Ubiquity: библиотекарь вернула серьезную ошибку для запуска загрузки. Error Domain = BRCloudDocsErrorDomain Code = 5 "Операция не может быть завершена (ошибка BRCloudDocsErrorDomain 5 - Нет документа по URL-адресу)"

NSPersistentStoreCoordinatorStoresDidChangeNotification никогда не вызывается перед ошибкой.

Это мой код для уведомления:

- (void)processStoresDidChange:(NSNotification *)notification
{
      NSLog(@"processStoresDidChange");
      // Post notification to trigger UI updates

      // Check type of transition
      NSNumber *type = [notification.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey];

      //NSLog(@" userInfo is %@", notification.userInfo);
      //NSLog(@" transition type is %@", type);

      if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted) {

          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted");

      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeAccountAdded) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeAccountAdded");
      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeAccountRemoved) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeAccountRemoved");
      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeContentRemoved) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeContentRemoved");
      }

      [[NSOperationQueue mainQueue] addOperationWithBlock:^ {

          if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeContentRemoved) {

              [self deleteLocalStore];
              _persistentStoreCoordinator = nil;
              _managedObjectContext = nil;

              NSLog(@" iCloud store was removed! Wait for empty store");
          }

          // Refresh user Interface
          [[NSNotificationCenter defaultCenter] postNotificationName:@"notiIcloud" object:@"storeChanged"];
      }];

}

Как я могу обнаружить, что содержимое iCloud было удалено и не удалось получить ошибку выше?

4b9b3361

Ответ 1

Мне кажется, вы не можете использовать соответствующие интерфейсы.

Вы можете перемещать файлы в iCloud и из них без использования методов NSPersistentStore. Часто соответствующее действие также должно быть обернуто вызовом NSFileCoordinator.

Вот что я делаю на iOS. toLocal указывает перемещение в или из локального (устройства) хранилища:

//
/// Move file between stores (assumed no name clash).
/// Return new URL.
/// Note: cloud file moves are asynchronous.
///
- (NSURL *)moveStoreFile:(NSURL *)url toRootURL:(NSURL *)rootURL toLocal:(BOOL)toLocal
{
    NSURL *newURL = rootURL;
    if (!toLocal)
        newURL = [newURL URLByAppendingPathComponent:@"Documents"];
    newURL = [newURL URLByAppendingPathComponent:url.lastPathComponent];

    [self backupFile:url isLocal:!toLocal completionHandler:
     ^(BOOL success) {

        MRLOG(@"MOVE %s %@ to %s %@", toLocal? "icloud" : "local", [url lastPathComponent],
              toLocal? "local" : "icloud", [newURL lastPathComponent]);

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
        ^{
           NSError *error;
           // setUbiquitous does file coordination
           BOOL msuccess = [[NSFileManager defaultManager]
                           setUbiquitous:!toLocal itemAtURL:url destinationURL:newURL error:&error];

           dispatch_async(dispatch_get_main_queue(),
           ^{
              if (!msuccess)
                  MRLOG(@"move failed: %@", error);
              [self.delegate moveStoreFileCompletedWithStatus:success error:error];
           });
        });
    }];

    return newURL;
}

Для удаления требуется явная координация файлов:

///
/// Purge backup file (really delete it).
/// Note: cloud deletes are asynchronous.
///
- (void)purgeFile:(NSURL *)url isLocal:(BOOL)isLocal
{
    MRLOG(@"PURGE %s %@", isLocal? "local" : "icloud", [url lastPathComponent]);

    if (isLocal)
        [self removeFile:url];
    else
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
        ^{
            NSError *coordinationError;
            NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];

            [coordinator coordinateWritingItemAtURL:url options:NSFileCoordinatorWritingForDeleting
                                                    error:&coordinationError
                                     byAccessor:
             ^(NSURL* writingURL)
             {
                 [self removeFile:writingURL];
             }];
            if (coordinationError) {
                MRLOG(@"coordination error: %@", coordinationError);
                [(SSApplication *)[SSApplication sharedApplication] fileErrorAlert:coordinationError];
            }
        });
}

Чтобы обнаружить изменения в файлах, вам нужно использовать NSMetadataQuery и добавить наблюдателя для NSMetadataQueryDidUpdateNotification.

Ответ 2

У меня была аналогичная проблема с синхронизацией iOS с моим Mac.

Эта ошибка 5 на самом деле означает, что есть проблема с вашим хранилищем.

Вы можете выполнить следующие действия:

  • На iOS вам нужно reset содержимое вашего симулятора, а также очистите содержимое iCloud (только для вашего приложения).

Вы можете reset ваш контент iCloud, позвонив (извините, что у меня есть это в Swift):

try! NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(storeURL, options: self.options)
  • В Mac OS вам нужно удалить содержимое этой папки " CoreDataUbiquitySupport/YourAppId". Это в основном ваш постоянный хранилище основных данных (например: файл SQLite).

Эта проблема возникает, когда модель (файл xcdatamodeld) изменилась, и существующие данные не были очищены. В результате у вас появилась новая модель, связанная с данными, сгенерированными в старой модели, и вы пытаетесь синхронизировать этот микс между устройствами...

В будущем, чтобы избежать проблем, связанных с Model, взгляните на моделирование версий данных и переноса данных. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

Надеюсь, что он решает вашу проблему.