jus*_*tin 38 iphone core-data ios5 icloud
我将开始更新这个,以帮助那些寻求使用它作为他们自己的个人代码的参考.
最新更新
另一个有助于正确设置的补充
@property (nonatomic, readwrite) BOOL unlocked;到AppDelegate.h
和@synthesize unlocked;到AppDelegate.m.然后我改变了我的-
(NSPersistentStoreCoordinator *)persistentStoreCoordinator方法以及我的- (void)mergeChangesFrom_iCloud方法,两者都将在下面显示(在持久存储设置的中间和iCloud合并方法的底部).本质上,我告诉应用程序阻止iCloud合并数据,直到应用程序设置其持久存储.否则,您将看到应用程序因无法读取的故障而崩溃.以下是我设置persistentStoreCoordinator的方法:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
// here is where you declare the persistent store is not prepared;
self.unlocked = NO;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Maintain_My_Car.sqlite"];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *options = nil;
NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"data"];
if (coreDataCloudContent.length != 0) {
// iCloud enabled;
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, @"<bundleIdentifier>.store", NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, nil];
} else {
// iCloud not enabled;
options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
}
NSError *error = nil;
[psc lock];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(@"bad things %@ %@", error, [error userInfo]);
abort();
}
[psc unlock];
// the store is now prepared and ready for iCloud to import data;
self.unlocked = YES;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"iCloud persistent store added");
[[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil];
});
});
return __persistentStoreCoordinator;
}
Run Code Online (Sandbox Code Playgroud)
<myAppKey>并且<bundleIdentifier>是实际值的,当然.我只是为了共享这段代码而屏蔽它们.
我知道有些人仍然遇到麻烦,并且可能正在使用这个问题作为如何设置他们自己的支持iCloud的核心数据应用程序的参考,所以每当我更改我的个人代码时我想更新它,确保所有人都可以使用适合我的代码.在此更新中,我将初始cloudURL更改[fileManager URLForUbiquityContainerIdentifier:@"<TeamIdentifier>.<bundleIdentifier>"]为[fileManager URLForUbiquityContainerIdentifier:nil],确保从权利文件中收集容器信息.
其他方法
_notificationArray定义如下:
@property (nonatomice, strong) NSMutableArray *notificationArray;
@synthesize notificationArray = _notificationArray;
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
if (self.unlocked) {
NSManagedObjectContext *moc = [self managedObjectContext];
if (self.notificationArray.count != 0) {
for (NSNotification *note in _notificationArray) {
[moc performBlock:^{
[self mergeiCloudChanges:note forContext:moc];
}];
}
[_notificationArray removeAllObjects];
[moc performBlock:^{
[self mergeiCloudChanges:notification forContext:moc];
}];
} else {
[moc performBlock:^{
[self mergeiCloudChanges:notification forContext:moc];
}];
}
} else {
if (_notificationArray == nil) {
_notificationArray = [[NSMutableArray alloc] init];
}
[_notificationArray addObject:notification];
}
}
- (void)resetStore {
[self saveContext];
__persistentStoreCoordinator = nil;
__managedObjectContext = nil;
// reset the managedObjectContext for your program as you would in application:didFinishLaunchingWithOptions:
myMainView.managedObjectContext = [self managedObjectContext];
// the example above will rebuild the MOC and PSC for you with the new parameters in mind;
}
Run Code Online (Sandbox Code Playgroud)
然后是mergeiCloudChanges:forContext:方法:
- (void)mergeiCloudChanges:(NSNotification *)note forContext:(NSManagedObjectContext *)moc {
// below are a few logs you can run to see what is being done and when;
NSLog(@"insert %@", [[note userInfo] valueForKey:@"inserted"]);
NSLog(@"delete %@", [[note userInfo] valueForKey:@"deleted"]);
NSLog(@"update %@", [[note userInfo] valueForKey:@"updated"]);
[moc mergeChangesFromContextDidSaveNotification:note];
NSNotification *refreshNotification = [NSNotification notificationWithName:@"RefreshAllViews" object:self userInfo:[note userInfo]];
[[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
// do any additional work here;
}
Run Code Online (Sandbox Code Playgroud)
最初的问题
在iOS 5.0.1上使用iCloud,我偶尔会收到与持久存储有关的错误.我将通过实验找到新信息继续更新,但到目前为止,我提供的解决方案是我可以让应用程序再次正常工作的唯一方法(不幸的是jlstrecker的解决方案对我不起作用)开始看到错误,如下所示:
-NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error :: CoreData:Ubiquity:尝试读取ubiquity root url时出错:file://localhost/private/var/mobile/Library/Mobile%20Documents/./data/.错误:错误Domain = LibrarianErrorDomain Code = 1"操作无法完成.(LibrarianErrorDomain错误1 - 无法启动项目下载.)"UserInfo = 0x176000 {NSURL = file:// localhost/private/var/mobile/Library /Mobile%20Documents/./data/,NSDescription =无法启动项目下载.}
对于我的生活,我无法弄清楚为什么我突然看到这个或如何让它停止.我从两台设备上删除了应用程序,删除了之前同步的iCloud数据,并删除了有关应用程序备份的所有数据.我重新启动了Xcode,重新启动了两个设备,清理了Xcode项目,但没有任何东西阻止错误显示.我以前从未见过这个错误,并且没有在线找到任何关于如何固定它的运气.
该应用程序崩溃在这里:
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(@"bad things %@ %@", error, [error userInfo]);
abort();
}
Run Code Online (Sandbox Code Playgroud)
日志永远不会被击中,也不会被中止.我只是看到上面的错误,应用程序本身变得没有响应.如果有人能帮助我指出正确的方向,我将非常感激.
以前的问题/问题
即使从测试版更新到5.0.1的公开版本之后,这似乎仍在继续.它最后一次发生在我之后是在更改我的托管上下文数据模型之后.考虑到我还没有发布应用程序,我没有费心合并新版本的模型.我刚刚删除并重新安装了我的设备上的应用程序,但后来它拒绝配合存储在iCloud容器中的数据,我的意思是我收到了商店无法下载项目的错误.我想这是由于数据模型类型的冲突,这是完全合理的.因此,您似乎只需要摆脱iCloud容器中的数据而不必删除容器.删除iCloud数据似乎会破坏所有内容,实质上是禁用容器和App ID.由于它似乎更简单,我尝试按照jlstrecker的建议创建一个新容器,但不幸的是,这根本没有用.所以再一次,我必须完成我在答案中概述的步骤,这再次成功.但考虑到每次必须创建新的App ID和更新配置文件是多么烦人,我认为最好更新我学到的东西,以便缩小原因并获得更快的解决方案.
通过iCloud>存储和备份>管理存储,然后删除应用程序似乎是清空数据的最佳解决方案,但这样做似乎破坏了容器,导致上面的错误.成功完成此操作后,无论多少次删除应用程序并将其重新安装到设备上(使其看起来像是第一次出现在设备上并希望重新创建容器),我永远无法让应用程序显示再次在"文档和数据"列表中.这有点令人担忧,如果它意味着任何从他们的iCloud删除数据的人意味着iCloud将不再适用于该应用程序.到目前为止,我只在应用程序上使用开发配置文件,因此使用分发配置文件可能会有所不同,但我必须先测试一下,然后才能确定.
我希望这些新的更新可以帮助任何可能在设置商店时遇到问题的人.到目前为止,它一直对我很有用.如果我找到更好的解决方案,或者只是让过程更加无缝的任何事情,我一定会更新更新.
jus*_*tin 13
更新了重新同步设备的答案 几个月的修修补补让我弄清楚(我相信)根深蒂固的问题是什么.问题是让设备在失去同步后再次互相交谈.我不能确定是什么导致这种情况,但我怀疑是事务日志已损坏,或者(更有可能)重新创建日志容器.这就像设备A发布更改到容器A和设备B执行相同操作,而不是发布到容器C,他们可以读取/写入日志.
现在我们知道了问题,这是一个创建解决方案的问题.更多的修补让我得到了以下内容.我有一个名为的方法resetiCloudSync:(BOOL)isSource,它是我原始问题中上述方法的修改版本.
- (void)resetiCloudSync:(BOOL)isSource {
NSLog(@"reset sync source %d", isSource);
NSManagedObjectContext *moc = self.managedObjectContext;
if (isSource) {
// remove data from app's cloud account, then repopulate with copy of existing data;
// find your log transaction container;
NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"store"];
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
NSError *error = nil;
// remove the old log transaction container and it's logs;
[[NSFileManager defaultManager] removeItemAtURL:cloudURL error:&error];
// rebuild the container to insert the "new" data into;
if ([[NSFileManager defaultManager] createFileAtPath:coreDataCloudContent contents:nil attributes:nil]) {
// this will differ for everyone else. here i set up an array that stores the core data objects that are to-many relationships;
NSArray *keyArray = [NSArray arrayWithObjects:@"addedFields", @"mileages", @"parts", @"repairEvents", nil];
// create a request to temporarily store the objects you need to replicate;
// my heirarchy starts with vehicles as parent entities with many attributes and relationships (both to-one and to-many);
// as this format is a mix of just about everything, it works great for example purposes;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Vehicle" inManagedObjectContext:moc];
[request setEntity:entity];
NSError *error = nil;
NSArray *vehicles = [moc executeFetchRequest:request error:&error];
for (NSManagedObject *object in vehicles) {
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:object.entity.name inManagedObjectContext:moc];
// check regular values;
for (NSString *key in object.entity.attributesByName.allKeys) {
[newObject setValue:[object valueForKey:key] forKey:key];
}
// check relationships;
NSMutableSet *relSet = [[NSMutableSet alloc] init];
for (NSString *key in object.entity.relationshipsByName.allKeys) {
[relSet removeAllObjects];
// check to see relationship exists;
if ([object valueForKey:key] != nil) {
// check to see if relationship is to-many;
if ([keyArray containsObject:key]) {
for (NSManagedObject *toManyObject in [object valueForKey:key]) {
[relSet addObject:toManyObject];
}
} else {
[relSet addObject:[object valueForKey:key]];
}
// cycle through objects;
for (NSManagedObject *subObject in relSet) {
NSManagedObject *newSubObject = [NSEntityDescription insertNewObjectForEntityForName:subObject.entity.name inManagedObjectContext:moc];
// check sub values;
for (NSString *subKey in subObject.entity.attributesByName.allKeys) {
NSLog(@"subkey %@", subKey);
[newSubObject setValue:[subObject valueForKey:subKey] forKey:subKey];
}
// check sub relationships;
for (NSString *subRel in subObject.entity.relationshipsByName.allKeys) {
NSLog(@"sub relationship %@", subRel);
// set up any additional checks if necessary;
[newSubObject setValue:newObject forKey:subRel];
}
}
}
}
[moc deleteObject:object];
}
[self resetStore];
}
} else {
// here we remove all data from the current device to populate with data pushed to cloud from other device;
for (NSManagedObject *object in moc.registeredObjects) {
[moc deleteObject:object];
}
}
[[[UIAlertView alloc] initWithTitle:@"Sync has been reset" message:nil delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil] show];
}
Run Code Online (Sandbox Code Playgroud)
在这段代码中,我有两条不同的路径.一种是用于不同步且需要从源设备导入数据的设备.所有这些路径都清楚了内存,以便为应该在其中的数据做好准备.
other(isSource = YES)路径做了很多事情.通常,它会删除损坏的容器.然后它创建一个新容器(让日志有一个驻留的位置).最后,它搜索父实体并复制它们.这样做是使用应该存在的信息重新填充事务日志容器.然后,您需要删除原始实体,以便您没有重复项.最后,重置持久性存储以"刷新"应用程序的核心数据并更新所有视图和fetchedResultsControllers.
我可以证明这很有效.我已经清除了isSource = NO几个月来没有与主设备(数据所在的地方)通信的设备()的数据.然后,我从主设备推送数据并愉快地观看,因为我的所有数据都在几秒钟内出现.
再次,请随意引用并分享给任何与iCloud同步有问题的人.
原始问题的答案,在iOS 5.1发布后不再受影响,修复了在您的设置中删除应用程序的iCloud存储后崩溃
经过许多小时尝试任何事情和所有事情来解决这个问题后,我尝试创建一个新的App ID,更新应用程序的相关配置文件,在iCloud容器字段周围更改以匹配新配置文件,一切都可以再次运行.我仍然不知道为什么会发生这种情况,但似乎与该应用程序ID相关联的iCloud存储已损坏?
所以底线是如果其他人发生这种情况,请遵循以下步骤,你应该做得很好:
<bundleIdentifier>适合新App ID的所有实例(这些将在您的主应用程序摘要页面,iCloud容器和iCloud Key-Value Store的权利,以及您正在创建持久性存储的AppDelegate文件中,就像在我的代码中一样以上).另一个澄清:在iOS 6.0.1和6.1 beta 2上测试设备时发生了类似的情况.
如@Slev所述,它在iOS 5.1中并未完全修复.一个设备在尝试访问iCloud上的持久存储时会完全冻结大约80秒,但实际上永远不会访问存储在那里的信息.
我相信这是由于设备操作系统中的日志文件损坏.删除设备上的应用程序或iCloud数据无法解决冻结/无法访问iCloud存储的问题.
我发现的一个修复是在设备上reset all settings(擦除所有内容也可以).settings->general->reset.
只有这样我才能再次使用我的应用程序访问iCloud上的数据.我希望这有助于其他任何人来到这里寻找一个非常令人沮丧的bug的解决方案.
| 归档时间: |
|
| 查看次数: |
12975 次 |
| 最近记录: |