iOS 核心数据存储在 iCloud 中以与其他设备同步
Posted
技术标签:
【中文标题】iOS 核心数据存储在 iCloud 中以与其他设备同步【英文标题】:iOS core data store in iCloud to sync with other devices 【发布时间】:2012-07-12 15:05:58 【问题描述】:我一直在考虑让 Core Data 与 iCloud 一起使用,以便可以从多个设备访问人们的数据。 我一直在尽可能地学习本教程 (http://timroadley.com/) 并使用我的应用程序,但我遇到了一些问题。
问题 1:当应用关闭(不仅仅是暂停)并重新打开时,表格中没有显示任何内容,因此之前的数据似乎没有被加载
问题 2:我不知道是 iCloud 还是应用程序,但同步更改需要时间。我在一台设备上进行了更改,需要 2-3 分钟才能在另一台设备上显示
谁能帮我解决这两个问题
此代码在我的 AppDelegate 中
- (void)saveContext
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
- (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 = @"MF4HVVX5DS.Desbrina.Medicine-Tracker";
// ** Note: if you adapt this code for your own use, you should change this variable:
NSString *dataFileName = @"Medicine-Tracker.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);
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;
- (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;
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];
];
这是在主视图控制器中
- (void)reloadFetchedResults:(NSNotification*)note
NSLog(@"Underlying data changed ... refreshing!");
[self fetchedResultsController];
NSLog(@"%@", note.object);
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadFetchedResults:)
name:@"SomethingChanged"
object:[[UIApplication sharedApplication] delegate]];
- (void)viewDidUnload
[super viewDidUnload];
// Release any retained subviews of the main view.
[[NSNotificationCenter defaultCenter] removeObserver:self];
【问题讨论】:
【参考方案1】:问题 1:当应用关闭(不仅仅是暂停)并重新打开时,表格中没有显示任何内容,因此之前的数据似乎没有被加载
检查是否将所有数据保存到托管对象上下文中。这不应该是核心数据问题
问题 2:我不知道是 iCloud 还是应用程序,但同步更改需要时间。我在一台设备上进行了更改,需要 2-3 分钟才能在另一台设备上显示
2-3 分钟真的很好。有时可能需要数小时才能使数据处于完全同步状态。至少我半年前经历过。
请记住,iCloud 不适合在多台设备上同时使用。它宁可提供最终一致性之类的东西。
【讨论】:
在进行更改或退出应用程序时,一切都会调用保存上下文。我已将保存上下文添加到第一篇文章【参考方案2】:在您的示例中: NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil]; 用你的 NSString *iCloudEnabledAppID = @"MF4HVVX5DS.Desbrina.Medicine-Tracker"; 作为 URLForUbiquityContainerIdentifier
应该是这样的:
NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier: iCloudEnabledAppID];
【讨论】:
以上是关于iOS 核心数据存储在 iCloud 中以与其他设备同步的主要内容,如果未能解决你的问题,请参考以下文章