【发布时间】:2014-11-16 06:51:09
【问题描述】:
有人在 Xcode 6 和 iOS 8 上安装了 iCloud 核心数据同步设置吗? (希望这不是重复的帖子)
iCloud Core Data 存储选项去了哪里?
我记得 Core Data 有一个称为 Core Data 存储的额外存储选项,但现在在 Xcode 6 中,当我在 Xcode 6 中启用 iCloud 切换时,它似乎只显示键值和文档存储。
背景资料
- 新的 iPad 应用程序
- Xcode 6
- 以最低版本 iOS 7 为目标,但希望它也适用于 iOS 8? (我们可以将 iOS 8 设置为最低)
- 想要使用 iCloud Core Data 存储而不是键值或文档存储。
- 已在“设置”>“iCloud”中为模拟器和 iPad 设备登录相同的 Apple 帐户
- 我用于对应用程序进行代码签名的配置文件已为开发和分发启用了 iCloud(由 Xcode 自动启用)
我的设置
到目前为止,我不知道我是否正确设置了 Core Data iCloud。
Xcode 似乎已经在 iOS 开发者门户中设置了 iCloud 容器:
iCloud.com.xxxxxx.xxxxxxxx (note: I've replaced the actual strings with xxxx here)
我的 Xcode 6 iCloud“服务”列表旁边没有勾选:
- 键值存储
- iCloud 文档
- CloudKit
我们现在应该使用哪一个,因为它没有将“核心数据”列为存储选项?
在“服务”正下方的“容器”中,以下选项显示为灰色:
- 使用默认容器(默认勾选此项)
- 指定自定义容器
- iCloud.com.xxxxxxxxxx.xxxxxxxxx(同样,用 xxxx 替换了真实的标识符)
我无法选择任何选项,这似乎迫使我“使用默认容器”。
最后,Xcode 似乎显示以下标记:
- 将“iCloud”权利添加到您的 App ID
- 将“iCloud 容器”权利添加到您的 App ID
- 将“iCloud”权利添加到您的权利文件中
- 链接 CloudKit.framework
所以通过 Xcode 自己的自动化过程,它为我设置了一切。
参考代码
好的,所以我仔细阅读并注意到这里写了一个 iCloud 堆栈:
https://github.com/mluisbrown/iCloudCoreDataStack
我已经获取了必要的代码并尝试适应我的核心数据管理器单例:
DataManager.h 文件
+ (id)sharedModel;
+ (ALAssetsLibrary *)sharedLibrary;
@property (nonatomic, readonly) NSManagedObjectContext *mainContext;
@property (nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
- (NSString *)modelName;
- (NSString *)pathToModel;
- (NSString *)storeFilename;
- (NSString *)pathToLocalStore;
#pragma mark - Entity Fetching Methods -
-(NSArray *)fetchEntityOfType:(NSString *)entityType UsingPredicated:(NSPredicate *)predicate sortBy:(NSString *)sortKey ascendingOrder:(BOOL)ascendingOrder;
DataManager.m 文件
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
- (NSString *)documentsDirectory;
@end
@implementation MLSAlbumsDataModel
@synthesize managedObjectModel = _managedObjectModel;
@synthesize storeCoordinator = _storeCoordinator;
@synthesize mainContext = _mainContext;
+ (id)sharedModel {
static MLSAlbumsDataModel *__instance = nil;
if (__instance == nil) {
__instance = [[MLSAlbumsDataModel alloc] init];
}
return __instance;
}
+ (ALAssetsLibrary *)sharedLibrary {
static ALAssetsLibrary *__instance = nil;
if (__instance == nil) {
__instance = [[ALAssetsLibrary alloc] init];
}
return __instance;
}
- (NSString *)modelName {
return @"Albums";
}
- (NSString *)pathToModel {
return [[NSBundle mainBundle] pathForResource:[self modelName] ofType:@"momd"];
}
- (NSString *)storeFilename {
return [[self modelName] stringByAppendingPathExtension:@"sqlite"];
}
- (NSString *)pathToLocalStore {
return [[self documentsDirectory] stringByAppendingPathComponent:[self storeFilename]];
}
- (NSString *)documentsDirectory {
NSString *documentsDirectory = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
return documentsDirectory;
}
- (NSManagedObjectContext *)mainContext {
if(_mainContext == nil) {
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
// setup persistent store coordinator
DLog(@"SQLITE STORE PATH: %@", [self pathToLocalStore]);
NSURL *storeURL = [NSURL fileURLWithPath:[self pathToLocalStore]];
//_mainContext.persistentStoreCoordinator = [self storeCoordinator];
_mainContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
__weak NSPersistentStoreCoordinator *psc = self.mainContext.persistentStoreCoordinator;
// iCloud notification subscriptions
NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
[dc addObserver:self
selector:@selector(storesWillChange:)
name:NSPersistentStoreCoordinatorStoresWillChangeNotification
object:psc];
[dc addObserver:self
selector:@selector(storesDidChange:)
name:NSPersistentStoreCoordinatorStoresDidChangeNotification
object:psc];
[dc addObserver:self
selector:@selector(persistentStoreDidImportUbiquitousContentChanges:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:psc];
NSError* error;
// the only difference in this call that makes the store an iCloud enabled store
// is the NSPersistentStoreUbiquitousContentNameKey in options. I use "iCloudStore"
// but you can use what you like. For a non-iCloud enabled store, I pass "nil" for options.
// Note that the store URL is the same regardless of whether you're using iCloud or not.
// If you create a non-iCloud enabled store, it will be created in the App's Documents directory.
// An iCloud enabled store will be created below a directory called CoreDataUbiquitySupport
// in your App's Documents directory
[self.mainContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" }
error:&error];
if (error) {
NSLog(@"error: %@", error);
}
_storeCoordinator = self.mainContext.persistentStoreCoordinator;
}
return _mainContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if(_managedObjectModel == nil) {
NSURL *storeURL = [NSURL fileURLWithPath:[self pathToModel]];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:storeURL];
}
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)storeCoordinator {
if (_storeCoordinator == nil) {
// -----------------------------------------------------------------------------------------------------------------------------
// Code moved to managed object context code above
// -----------------------------------------------------------------------------------------------------------------------------
/*
DLog(@"SQLITE STORE PATH: %@", [self pathToLocalStore]);
NSURL *storeURL = [NSURL fileURLWithPath:[self pathToLocalStore]];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:error forKey:NSUnderlyingErrorKey];
NSString *reason = @"Could not create persistent store";
NSException *exc = [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:userInfo];
@throw exc;
}
_storeCoordinator = psc;
*/
}
return _storeCoordinator;
}
#pragma mark - iCloud Related Methods -
// Subscribe to NSPersistentStoreDidImportUbiquitousContentChangesNotification
- (void)persistentStoreDidImportUbiquitousContentChanges:(NSNotification*)note
{
NSLog(@"%s", __PRETTY_FUNCTION__);
NSLog(@"%@", note.userInfo.description);
NSManagedObjectContext *moc = self.mainContext;
[moc performBlock:^{
[moc mergeChangesFromContextDidSaveNotification:note];
DLog(@"NSPersistentStoreDidImportUbiquitousContentChangesNotification executed");
/*
// you may want to post a notification here so that which ever part of your app
// needs to can react appropriately to what was merged.
// An exmaple of how to iterate over what was merged follows, although I wouldn't
// recommend doing it here. Better handle it in a delegate or use notifications.
// Note that the notification contains NSManagedObjectIDs
// and not NSManagedObjects.
NSDictionary *changes = note.userInfo;
NSMutableSet *allChanges = [NSMutableSet new];
[allChanges unionSet:changes[NSInsertedObjectsKey]];
[allChanges unionSet:changes[NSUpdatedObjectsKey]];
[allChanges unionSet:changes[NSDeletedObjectsKey]];
for (NSManagedObjectID *objID in allChanges) {
// do whatever you need to with the NSManagedObjectID
// you can retrieve the object from with [moc objectWithID:objID]
}
*/
}];
}
// Subscribe to NSPersistentStoreCoordinatorStoresWillChangeNotification
// most likely to be called if the user enables / disables iCloud
// (either globally, or just for your app) or if the user changes
// iCloud accounts.
- (void)storesWillChange:(NSNotification *)note {
NSManagedObjectContext *moc = self.mainContext;
[moc performBlockAndWait:^{
NSError *error = nil;
if ([moc hasChanges]) {
[moc save:&error];
}
[moc reset];
}];
// now reset your UI to be prepared for a totally different
// set of data (eg, popToRootViewControllerAnimated:)
// but don't load any new data yet.
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifCoreDataStoreWillChange" object:nil];
DLog(@"storeWillChange notification fire");
}
// Subscribe to NSPersistentStoreCoordinatorStoresDidChangeNotification
- (void)storesDidChange:(NSNotification *)note
{
// here is when you can refresh your UI and
// load new data from the new store
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifCoreDataStoreDidChange" object:nil];
DLog(@"storeDidChange notification fire");
}
#pragma mark - Entity Fetching Methods -
-(NSArray *)fetchEntityOfType:(NSString *)entityType UsingPredicated:(NSPredicate *)predicate sortBy:(NSString *)sortKey ascendingOrder:(BOOL)ascendingOrder
{
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityType inManagedObjectContext:[[MLSAlbumsDataModel sharedModel] mainContext]];
NSSortDescriptor *sortDescriptor = nil;
if(sortKey)
{
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:ascendingOrder];
}
else
{
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"updatedAt" ascending:ascendingOrder];
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entityDescription;
if(predicate)
{
request.predicate = predicate;
}
request.sortDescriptors = @[sortDescriptor];
NSError *error = nil;
NSArray *results = [[[MLSAlbumsDataModel sharedModel] mainContext] executeFetchRequest:request error:&error];
if(results == nil)
{
DLog(@"Error getting entity of type '%@' using predicate '%@', sortKey '%@' ascendingOrder %d", entityType, predicate, sortKey, ascendingOrder);
}
return results;
}
我的观察
我尝试在 iPad 模拟器(我相信它是 iOS 8 模拟器)和运行 iOS 7.x 的 iPad 设备上运行该应用程序
我在模拟器上使用用户输入的名称创建了一个相册,但我没有看到 iPad 设备显示新创建的相册。我也试过调换角色,iPad设备创建,iOS模拟器也没有结果。
我确实看到了我的日志消息:
storeDidChange notification fire
SQLITE STORE PATH: /Users/xxxxxxx/Library/Developer/CoreSimulator/Devices/3DC17576-92E9-4EAF-B77A-41340AE28F92/data/Containers/Data/Application/E51085CE-3772-4DF1-A503-1C243497091A/Documents/Albums.sqlite
如果我在模拟器中最小化应用程序并再次打开它(在 Xcode 中不按停止按钮),我会看到以下消息:
-[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](808): CoreData: Ubiquity: nobody~sim301AE3E8-16B2-5A08-917D-7B55D1879BE4:iCloudStore
Using local storage: 1
我读到“使用本地存储:0”是理想的状态吗?并且 1 表示本地设备数据存储而不是 iCloud 数据存储。
当我创建一个相册时,保存它,停止模拟器,然后重新启动应用程序,我的相册消失了,但在我创建一个新相册后,之前的所有相册又神奇地重新出现了。这有点奇怪。如果我不使用 iCloud 并将我的代码恢复到以前的设置,我可以创建并正常查看我的相册,无论我是否最小化我的应用程序或重新启动应用程序,但是我没有我需要的 iCloud 同步.
我有没有在任何地方犯过错误?
很抱歉,这篇文章很长,但有没有人让 iCloud 为 iOS 8 和 Xcode 6 工作?
我真的需要一些帮助。
额外问题
1) iOS 8 是否需要使用此容器标识符? (Xcode 6 为我生成):
com.apple.developer.icloud-container-identifiers
这不是 iOS 7 的样子,对吧? iOS 7 更像:
com.apple.developer.ubiquity-container-identifiers
2) 我需要一个 iCloud Drive 帐户才能使用它吗?
超级困惑@_@
【问题讨论】:
-
好问题 - 我希望我知道答案;事实上,我正在寻找相同的答案。我有一个使用带有 Core Data 和 iCloud 的 iOS 7 构建的应用程序,但我完全不确定如何处理 Xcode 6 和 iOS 8。
-
@Lavanya 我在下面发布了一个适用于我的 iOS 7 解决方案,看看是否对你有帮助。这个想法是您需要勾选“iCloud Documents”选项。 “用户默认容器”单选按钮选项很好。我还没有让 iOS 8 工作 =/
-
好的,我想我现在也为 iOS 8 解决了这个问题。它似乎对我有用。
标签: ios core-data ios8 icloud xcode6