2012-05-10 79 views
3

我有一個很難解決的問題。我有這種情況:Hard CoreData + iCloud場景

我的應用程序使用CoreData來存儲對象,我想實現設備之間的iCloud同步...和我的應用程序需要初始填充數據庫。

第一次啓動我的應用程序時,它會將我的數據庫填充到雲上,並將某些db字段標記爲「databaseInstalled」。這些字段也在雲中同步。

現在,當其他設備啓動應用程序的第一次,我希望能檢索字段「databaseInstalled」,以檢查是否注入與否的一些數據,但它是錯的......

如果databaseInstalled是假的,我們注入數據,如果databaseInstalled是真的,我們等待iCloud同步。

的問題是,我找回persistentStoreCoordinator異步地,因爲我不想阻止正在等待從iCloud中下載數據應用...

那麼如何才能知道一個先驗的,如果我需要填充數據庫還是已經填充到另一臺設備上,我只需從iCloud下載一個已安裝的設備?

- (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 = @"this is a secret!"; 

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

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

     NSString *iCloudDataDirectoryName = @"Data.nosync"; 
     NSString *iCloudLogsDirectoryName = @"Logs"; 
     NSFileManager *fileManager = [NSFileManager defaultManager];   
     NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName]; 
     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); 

      // da rimuovere 

      //[fileManager removeItemAtURL:iCloudLogsPath error:nil]; 
      #warning to remove 

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

      //[fileManager removeItemAtPath:iCloudData error:nil]; 
#warning to remove 

      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"); 
      NSLog(@"Local store: %@", localStore.path); 
      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(), ^{ 
      NSLog(@"iCloud routine completed."); 

      Setup *install = [[Setup alloc] init]; 

      if([install shouldMigrate]) { 
       HUD = [[MBProgressHUD alloc] initWithView:self.window.rootViewController.view]; 
       HUD.delegate = self; 
       HUD.labelText = NSLocalizedString(@"Sincronizzazione del database", nil); 
       [self.window.rootViewController.view addSubview:HUD]; 
       [HUD showWhileExecuting:@selector(installDatabase) onTarget:install withObject:nil animated:YES]; 
      } 
      else { 
       [[NSNotificationCenter defaultCenter] postNotificationName:@"setupCompleted" object:self]; 
      } 

      //[[NSNotificationCenter defaultCenter] postNotificationName:@"icloudCompleted" object:self userInfo:nil]; 
      [[NSNotificationCenter defaultCenter] postNotificationName:@"setupCompleted" object:self]; 
     }); 
    }); 

    return __persistentStoreCoordinator; 
} 

回答

0

直到完成與iCloud的同步後,才能知道iCloud中是否存在可用的數據。這意味着您有兩種選擇:

  • 讓用戶等待,直到完成同步。

  • 啓動時使用默認數據庫,並儘可能合併來自iCloud的更改。

隨着iCloud的,你需要本地數據和雲之間的數據有strategy for resolving conflicts,因爲你要處理的事實,用戶可能在同一時間更改多個設備上的數據。一旦你掌握了這一點,看起來非常清楚的是,上面的第二個選擇是更好的選擇:用戶可以立即開始使用你的應用程序,並且當雲中的數據可用時就會合並。

+0

數據從預填充數據庫中的量是很大的,從1K到4K的對象(記錄)。第二個選擇是它如何工作,但我有另一個問題...該應用程序是第一次注入對象來填充數據庫,同時它正在合併數據與iCloud ...並且我有一個異常(集合被靜音)在我的NSFetchedResultsController ...仍然令人不安... – Progeny

+0

我忘記了...選項2的主要問題是,我的數據庫是重複的,因爲兩個設備在iCloud上同步本地數據庫...合併本地數據庫從iCloud不刪除現有的記錄,但重複它的! – Progeny

+0

您回答基於文檔的應用程序而不是CoreData + iCloud。有沒有簡單的方法來解決預填充數據庫方案。至少我還沒有找到一個。主要的問題是,一旦用數據預填充數據庫,就會出現重複。 ObjectId將會不同。 –

0

我有完全相同的問題。

看看我的問題&我的回答是iCloud + CoreData - how to avoid pre-filled data duplication?

其實這是行不通的100%確定。如果你敢嘗試,我可以向你解釋你如何使它100%正確工作(儘管我還沒有嘗試過)。

考慮到您有很多數據來預先填充我的解決方案,現在可能會爲您解決。

+0

我很抱歉,但我不太喜歡你的解決方案。我在這些日子裏想的太多了,可能我發現了一些更好的東西。我們可以使用預先填充的CoreData數據庫(使用CoreData Editor/manager(?)等編輯器),並將整個數據庫複製到.nosyc目錄中,因爲我們需要所有記錄必須具有相同的UUID。 .nosync目錄的內容在雲中不同步,但該記錄上的新操作的事務日誌將同步。我認爲這是最好的方法。最後的問題是:如果Apple更改CoreData預生成(已生成和已複製)填充的數據庫會怎麼樣? – Progeny

+0

我的解決方案確實不完美。這正是我在有限的時間內設法解決的問題。你的解決方案是否完全適合你的任務?我的預填充數據是「類別」,我需要在不同設備上添加「項目」才能屬於同一類別。這是根據你的經驗發生的嗎?我沒有完全明白你最後的問題。你害怕Apple改變核心數據bd格式嗎? –

+0

3個月前我正在開發這個案例,所以我現在不記得每一個細節。但我認爲我也嘗試了您的方法,但我沒有獲得數據一致性 - 不同設備上的「類別」實際上是與iCLoud透視圖不同的實體。如果你報告你的結果,我將不勝感激。 –

0

沒有辦法來確定是否第一次打開數據存儲。至少不在iCloud Core Data存儲上。想想吧,iCloud也應該離線工作 - 也就是說,當用戶從互聯網斷開連接並在連接恢復時上傳時,所有更改都應該被緩衝。沒有辦法檢查數據存儲是否被初始化,而不會潛在地讓用戶等待幾分鐘(甚至如果設備離線),以詢問iCloud的數據存儲副本。

爲了解決這個問題,你需要遵循以下四個簡單的原則:

  • 有一種方法來刪除重複的預先填充記錄。
  • 有一種方法來識別預先填充的記錄並區分用戶輸入的記錄。
  • 每次從iCloud傳入新的事務記錄時都要運行重複數據刪除過程。
  • 只有每個設備/帳戶組合的種子數據記錄一次。

你可以在這裏閱讀更多詳細信息:http://cutecoder.org/programming/seeding-icloud-core-data/