2015-09-01 13 views
11

Mam aplikację dla OSX i iOS i oba używają iCloud do dzielenia się między nimi podstawowymi danymi. Kiedy wprowadzam zmiany w iPhonie, widzę je w aplikacji OSX, a NSPersistentStoreDidImportUbiquitousContentChangesNotification jest wywoływany w obu aplikacjach, więc iOS -> Mac działa dobrze. Ale kiedy wprowadzam pewne zmiany w aplikacji Mac, nie widzę ich na iPhonie. IPhone nigdy nie wywołuje NSPersistentStoreDidImportUbiquitousContentChangesNotification. Jedynym sposobem, w jaki mogę uzyskać zmiany na Macu, jest wprowadzenie pewnych zmian na iPhonie. Wtedy widzę wszystkie zmiany.Współdzielenie danych iCloud pomiędzy OSX i iOS

Integracja iCloud w obu projektach jest taka sama. Główny datamodel danych jest taki sam. Uprawnienia są również takie same.

Całe kod jest oparty na tej biblioteki: http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/

Dlaczego iPhone nie uzyskiwanie zmian Mac aż położę jakąś zmianę na iPhone? Jakieś pomysły?

Inną kwestią

W mojej aplikacji, gdy użytkownik pozwala sprawdzić, jeśli plik jest już w iCloud iCloud. Jeśli plik istnieje, użytkownik może wybrać przywrócenie podstawowych danych z pliku iCloud lub rozpocząć nową kopię z lokalnego magazynu. Aby rozpocząć nową kopię używam tego kodu:

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

ten sposób z jednego urządzenia wszystko wydaje się działać dobrze, ale gdy próbuję to z wieloma urządzeniami Dostaję jakieś błędy.

Przykład:

Mam dwa urządzenia. Pierwsza nie jest połączona z iCloud, a druga jest połączona z iCloud. W pierwszym urządzeniem, kiedy włączyć iCloud wybiorę startNewCopy a następnie w drugim urządzeniu otrzymuję ten błąd:

CoreData: Ubiquity: Librarian returned a serious error for starting downloads Error Domain=BRCloudDocsErrorDomain Code=5 "The operation couldn’t be completed. (BRCloudDocsErrorDomain error 5 - No document at URL)"

NSPersistentStoreCoordinatorStoresDidChangeNotification nigdy nie jest wywoływana przed błędem.

To jest mój kod na zgłoszenia:

- (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"]; 
     }]; 

} 

jaki sposób można wykryć, że treść została usunięta iCloud i uniknąć błędu powyżej?

+0

To tak naprawdę nie odpowiada na twoje pytanie, ale zrezygnowałem z używania Core Data z obsługą iCloud bardzo dawno temu. Potem napisałem własną synchronizację, ale było to trochę ograniczone. Potem znalazłem Ensembles (http://www.ensembles.io) i używam go od tego czasu. To było dla mnie niesamowite. Dostępna jest darmowa wersja open-source. –

+0

Thx za odpowiedź, ale wolę używać standardowego wsparcia dla iCloud od Apple. – user3065901

+0

Powodzenia! –

Odpowiedz

0

Wygląda na to, że możesz nie używać odpowiednich interfejsów.

Można przenosić pliki do i z systemu iCloud bez użycia metod NSPersistentStore. Często odpowiednią akcję należy również zawinąć w rozmowę NSFileCoordinator.

Oto, co robię na iOS. toLocal wskazuje na ruch do lub z lokalnego (urządzenie) Przechowywanie:

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

Kasowanie wymaga wyraźnej koordynacji pliku:

/// 
/// 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]; 
      } 
     }); 
} 

Aby wykryć zmiany w plikach, które trzeba użyć NSMetadataQuery i dodać obserwatora dla NSMetadataQueryDidUpdateNotification.

0

Miałem podobny problem z synchronizacją iOS z moim komputerem Mac.

Ten błąd 5 oznacza w rzeczywistości problem z Twoim sklepem.

Można spróbować kroki:

  • iOS trzeba przywrócić zawartość swojej Simulator, a także wyczyścić zawartość iCloud (tylko dla aplikacji).

Można zresetować zawartość iCloud przez wywołanie (przepraszam mam go w Swift):

try! NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(storeURL, options: self.options) 
  • W systemie Mac OS trzeba usunąć zawartość tego folderu "CoreDataUbiquitySupport/YourAppId ". Zasadniczo jest to Twój stały magazyn danych stałych (np .: plik SQLite).

Ten problem występuje, gdy model (plik xcdatamodeld) zmienił się, a istniejące dane nie zostały wyczyszczone. Kończy się posiadanie nowego modelu skojarzonego z danymi wygenerowanymi w starszym modelu i próbujesz zsynchronizować ten miks na różnych urządzeniach ...

W przyszłości, aby uniknąć problemu związanego z modelem, zapoznaj się z wersją modelu danych podstawowych i migracja danych. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

Mam nadzieję, że rozwiąże problem.

Powiązane problemy