有人在Xcode 6和iOS 8上设置了iCloud核心数据同步吗?(希望这不是重复的帖子)
iCloud核心数据存储选项去哪里了?
我记得Core Data有一个额外的存储选项叫做Core Data存储,但现在在Xcode 6中启用iCloud切换时,它似乎只显示键值和文档存储。
背景信息
- 新的iPad应用程序
- Xcode 6
- 目标最低版本为iOS 7,但希望它也适用于iOS 8?(我们可以将iOS 8设置为最低版本)
- 想要使用iCloud核心数据存储而不是键值或文档存储。
- 已经登录到Simulator和iPad设备的设置>iCloud中相同的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自动化的过程,它已经帮我设置好了一切。
参考代码
好的,我看了看这里写的:
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同步。我哪里犯了错吗?对于这篇长文章感到抱歉,但是有没有人让iOS 8和Xcode 6的iCloud工作?我真的需要一些帮助。
额外问题:
1)iOS 8是否需要使用此容器标识符?(Xcode 6为我生成):
com.apple.developer.icloud-container-identifiers
这不是iOS 7的样式,对吧?iOS 7的样式更像是:
com.apple.developer.ubiquity-container-identifiers
2) 在使用iCloud Drive之前,我需要一个iCloud Drive账户吗?
超级困惑 @_@