[英]Xcode 6 iOS 8 iCloud core data setup
Has anyone got iCloud core data syncing setup on Xcode 6 and iOS 8? 有没有人在Xcode 6和iOS 8上获得iCloud核心数据同步设置? (hopefully this isn't a duplicate post)
(希望这不是一个重复的帖子)
Where did iCloud Core Data storage option go? iCloud核心数据存储选项在哪里?
I remember Core Data had an extra storage option called Core Data storage, but now in Xcode 6 it only seems to show key-value and document storage when I enable the iCloud toggle in Xcode 6. 我记得Core Data有一个名为Core Data storage的额外存储选项,但现在在Xcode 6中,当我在Xcode 6中启用iCloud切换时,它似乎只显示键值和文档存储。
So far, I don't know if I've setup Core Data iCloud correctly. 到目前为止,我不知道我是否正确设置了Core Data iCloud。
Xcode appears to have setup the iCloud containers in the iOS Developer Portal: Xcode似乎已在iOS Developer Portal中设置了iCloud容器:
iCloud.com.xxxxxx.xxxxxxxx (note: I've replaced the actual strings with xxxx here)
My Xcode 6 iCloud "services" list shows no ticks next to: 我的Xcode 6 iCloud“服务”列表显示旁边没有刻度:
Which one should we be using now since it doesn't list "core data" as a storage option? 我们现在应该使用哪一个,因为它没有将“核心数据”列为存储选项?
In the "Containers" directly below the "services", it shows the following options greyed out: 在“服务”正下方的“容器”中,它显示以下灰色选项:
I can't choose any option, it seems to force me to "Use default container". 我无法选择任何选项,它似乎迫使我“使用默认容器”。
Finally, Xcode seems to show ticks for: 最后,Xcode似乎显示为:
So by Xcode's own automated process, it setup everything for me. 因此,通过Xcode自己的自动化流程,它为我设置了一切。
OK, so I read around and notice a iCloud stack written here: 好的,所以我四处阅读并注意到这里写的iCloud堆栈:
https://github.com/mluisbrown/iCloudCoreDataStack https://github.com/mluisbrown/iCloudCoreDataStack
I've taken the necessary code and tried to adapt to my Core Data manager singleton: 我已经采取了必要的代码并尝试适应我的核心数据管理器单例:
+ (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;
@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;
}
I tried to run the app on the iPad Simulator (I believe it is the iOS 8 simulator) and on iPad device running iOS 7.x 我试图在iPad模拟器上运行应用程序(我相信它是iOS 8模拟器)并在运行iOS 7.x的iPad设备上运行
I created an album with a user entered name on the simulator, but I am not seeing the iPad device showing the newly created album. 我在模拟器上创建了一个用户输入名称的相册,但我没有看到iPad设备显示新创建的相册。 I also tried reversing the roles, iPad device create, iOS simulator no results either.
我也试过逆转角色,iPad设备创建,iOS模拟器也没有结果。
I do see my log messages: 我确实看到了我的日志消息:
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
If I minimise the app in the simulator and open it again (without pressing the Stop button in Xcode), I see these message: 如果我在模拟器中最小化应用程序并再次打开它(不按Xcode中的“停止”按钮),我会看到以下消息:
-[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](808): CoreData: Ubiquity: nobody~sim301AE3E8-16B2-5A08-917D-7B55D1879BE4:iCloudStore
Using local storage: 1
I read that "Using local storage: 0" is what it should ideally be? 我读到“使用本地存储:0”是理想的应该是什么? and that 1 means local device data store rather than iCloud data store.
并且1表示本地设备数据存储而不是iCloud数据存储。
When I create an album, save it, stop the simulator, then start the app again, my albums disappears, but immediately after I create a new album, all the previous album reappear magically again. 当我创建一个专辑,保存它,停止模拟器,然后再次启动应用程序,我的专辑消失,但在我创建一个新专辑后,所有以前的专辑再次神奇地重新出现。 It's a bit odd.
这有点奇怪。 If I don't use iCloud and revert my code to previous setup, I can create and see my album fine, regardless of whether I minimise my app or not, or restart app, but then I don't have iCloud sync which I need.
如果我不使用iCloud并将我的代码恢复到之前的设置,我可以创建并查看我的相册,无论我是否最小化我的应用程序,或重新启动应用程序,但是我没有iCloud同步,我需要。
Have I made any mistakes anywhere? 我在任何地方都犯过任何错误吗?
Sorry for the long post but has anyone got iCloud working for iOS 8 and Xcode 6 ? 很抱歉这篇文章很长,但有没有人让iCloud为iOS 8和Xcode 6工作?
I could really use some help. 我真的可以使用一些帮助。
1) Does iOS 8 require the use of this container identifier ? 1)iOS 8是否需要使用此容器标识符? (which Xcode 6 generated for me):
(Xcode 6为我生成的):
com.apple.developer.icloud-container-identifiers
That's not what the iOS 7 one looks like right? 这不是iOS 7的样子吗? iOS 7 one is more like:
iOS 7更像是:
com.apple.developer.ubiquity-container-identifiers
2) Do I need an iCloud Drive account before it works? 2)在运行之前我需要一个iCloud Drive帐户吗?
Super confused @_@ 超级迷茫@ _ @
OK...then....lol. OK ...然后....大声笑。 I think I solved it.
我想我解决了。
New discovery. 新发现。 After skimming through this page:
浏览此页面后:
http://www.tuaw.com/2014/09/17/psa-do-not-upgrade-to-icloud-drive-during-ios-8-installation/ http://www.tuaw.com/2014/09/17/psa-do-not-upgrade-to-icloud-drive-during-ios-8-installation/
It says: 它说:
iCloud Drive is Apple's new and improved iCloud syncing and file storage feature that allows you to share documents between your iOS 8 devices and your Mac running OS X 10 Yosemite.
iCloud Drive是Apple新推出的改进的iCloud同步和文件存储功能,允许您在iOS 8设备和运行OS X 10 Yosemite的Mac之间共享文档。
So, I decided to bite the bullet and upgrade my iCloud account to iCloud drive (free to upgrade). 所以,我决定咬紧牙关并将我的iCloud帐户升级到iCloud驱动器(免费升级)。
After upgrading to iCloud drive, and re-ran my app with a few Xcode 6 changes, it's working now. 升级到iCloud驱动器后,通过一些Xcode 6更改重新运行我的应用程序,它现在正在运行。
Some Important Things to note: 需要注意的一些重要事项:
For the above reason, I changed to using a Ubiquity container with this pattern: 由于上述原因,我改为使用具有此模式的Ubiquity容器:
iCloud.$(CFBundleIdentifier) iCloud中。$(CFBundleIdentifier)
So something like: 所以类似于:
iCloud.com.xxxxxxxx.iCloudCoreDataDemo
Where "xxxxxxxx" is my company name identifier. 其中“xxxxxxxx”是我的公司名称标识符。
I made the above iCloud container by logging into my iOS Developer Center, perhaps you could just press the "+" sign inside Xcode 6 and enter one there, Xcode should automagically setup everything for you. 我通过登录我的iOS开发人员中心制作了上面的iCloud容器,也许您可以按Xcode 6中的“+”符号并在那里输入一个,Xcode应该自动为您设置一切。
One block of code I used to test to see if it's working is this: 我用来测试一段代码,看看它是否正常工作:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:self.modelURL];
self.managedObjectContext = self.persistentStack.managedObjectContext;
NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"iCloud.com.xxxxxxxxxx.iCloudCoreDataDemo"];
if(containerURL == nil)
{
NSLog(@"containerURL == nil");
}
else
{
NSLog(@"hurray?");
}
return YES;
}
If you see "hurray?" 如果你看到“欢呼?” then it's fine, you should also see this pattern of text in your Xcode console output:
那么没关系,您还应该在Xcode控制台输出中看到这种文本模式:
2014-10-07 17:37:23.196 iCloudCoreDataDemo[8104:130250] documentsDirectory = file:///Users/xxxxxxxx/Library/Developer/CoreSimulator/Devices/9FAFE881-13CA-4608-8BE6-728C793FAFFB/data/Containers/Data/Application/BC6CA07D-605A-4927-94AF-E9E21E204D2B/Documents/
2014-10-07 17:37:23.386 iCloudCoreDataDemo[8104:130250] storeDidChange
2014-10-07 17:37:23.390 iCloudCoreDataDemo[8104:130250] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](808): CoreData: Ubiquity: nobody~sim301AE3E8-16B2-5A08-917D-7B55D1879BE4:iCloudStore
Using local storage: 1
2014-10-07 17:37:23.402 iCloudCoreDataDemo[8104:130250] hurray?
2014-10-07 17:37:33.909 iCloudCoreDataDemo[8104:130250] storeWillChange
2014-10-07 17:37:33.933 iCloudCoreDataDemo[8104:130250] storeDidChange
2014-10-07 17:37:33.933 iCloudCoreDataDemo[8104:130330] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](808): CoreData: Ubiquity: nobody~sim301AE3E8-16B2-5A08-917D-7B55D1879BE4:iCloudStore
Using local storage: 0
Notice the two important lines: 请注意两个重要的行:
Using local storage: 1
later becomes: 后来成为:
Using local storage: 0
Local storage 1 means it's currently using local storage, while local storage 0 means it has moved the data to iCloud storage. 本地存储1表示它当前正在使用本地存储,而本地存储0表示它已将数据移动到iCloud存储。
I hope this benefits everyone else. 我希望这对其他人都有好处。
OK, so I've just discovered something and managed to get it working for iOS 7 only. 好的,所以我刚刚发现了一些东西,并设法让它只适用于iOS 7 。 I still haven't figured out how to do it in iOS 8 but I have noticed something important.
我仍然没有想到如何在iOS 8中做到这一点,但我注意到一些重要的事情。
On my iPhone 5 running iOS 8.0.2, I don't have the " Document & Data " option inside the iCloud settings menu anymore. 在运行iOS 8.0.2的iPhone 5上,iCloud设置菜单中没有“ 文档和数据 ”选项。
However, on my iPad running iOS 7, I DO see the "Document & Data" options. 但是,在运行iOS 7的iPad上,我看到了“文档和数据”选项。
Perhaps this is the reason why it doesn't work on iOS 8, we no longer have Document & Data storage ? 也许这就是它在iOS 8上不起作用的原因,我们不再拥有文档和数据存储?
Anyhow, here's what I discovered for iOS 7 only solution. 无论如何,这是我发现的iOS 7唯一解决方案。
I found this page here 我在这里找到了这个页面
https://developer.apple.com/library/ios/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html https://developer.apple.com/library/ios/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html
and one of the line says: 其中一条线说:
Sure enough, I went into my Xcode 6 project file and ticked the "iCloud Documents" option. 果然,我进入了我的Xcode 6项目文件并勾选了“iCloud Documents”选项。 This un-greyed the radio buttons, but I still left it at "Use default Containers".
这个没有灰色的单选按钮,但我仍然把它留在“使用默认容器”。
One thing I learned is that I need to init my PersistentStack in the appDelegate. 我学到的一件事是我需要在appDelegate中初始化我的PersistentStack。 Previously, I tried to init the persistent stack inside the +(id)sharedInstance method but it caused the iCloud to only sync the first time, so after initial load and sync, adding new record doesn't get synced afterwards.
以前,我尝试在+(id)sharedInstance方法中初始化持久性堆栈,但它导致iCloud仅在第一次同步,因此在初始加载和同步之后,添加新记录之后不会同步。
I rewrote a basic app and modified the persistent stack slightly: 我重写了一个基本应用程序并稍微修改了持久堆栈:
#import <UIKit/UIKit.h>
#import "PersistentStack.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) NSManagedObjectContext* managedObjectContext;
@property (nonatomic, strong) PersistentStack* persistentStack;
@end
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:self.modelURL];
self.managedObjectContext = self.persistentStack.managedObjectContext;
return YES;
}
...
- (NSURL*)storeURL
{
NSURL* documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
return [documentsDirectory URLByAppendingPathComponent:@"MyApp.sqlite"];
}
- (NSURL*)modelURL
{
return [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
}
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Book.h"
#import <UIKit/UIKit.h>
@interface PersistentStack : NSObject
+(id)sharedInstance;
- (id)initWithStoreURL:(NSURL *)storeURL modelURL:(NSURL *)modelURL;
@property (nonatomic,strong,readonly) NSManagedObjectContext *managedObjectContext;
#pragma mark - Regular Methods -
-(Book *)insertNewBookWithDate:(NSDate *)newDate;
-(void)deleteBook:(Book *)book;
-(NSArray *)fetchEntityOfType:(NSString *)entityType withPredicate:(NSPredicate *)predicate andSortKey:(NSString *)sortKey;
@end
#import "PersistentStack.h"
#import "AppDelegate.h"
@interface PersistentStack ()
@property (nonatomic,strong,readwrite) NSManagedObjectContext* managedObjectContext;
@property (nonatomic,strong) NSURL* modelURL;
@property (nonatomic,strong) NSURL* storeURL;
@end
@implementation PersistentStack
+(id)sharedInstance
{
static PersistentStack *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
sharedInstance = appDelegate.persistentStack;
});
return sharedInstance;
}
- (id)initWithStoreURL:(NSURL*)storeURL modelURL:(NSURL*)modelURL
{
self = [super init];
if (self) {
self.storeURL = storeURL;
self.modelURL = modelURL;
[self setupManagedObjectContext];
}
return self;
}
- (void)setupManagedObjectContext
{
self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
self.managedObjectContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
//__weak NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
// iCloud notification subscriptions
NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
[dc addObserver:self
selector:@selector(storesWillChange:)
name:NSPersistentStoreCoordinatorStoresWillChangeNotification
object:self.managedObjectContext.persistentStoreCoordinator];
[dc addObserver:self
selector:@selector(storesDidChange:)
name:NSPersistentStoreCoordinatorStoresDidChangeNotification
object:self.managedObjectContext.persistentStoreCoordinator];
[dc addObserver:self
selector:@selector(persistentStoreDidImportUbiquitousContentChanges:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:self.managedObjectContext.persistentStoreCoordinator];
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.managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:self.storeURL
options:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" }
error:&error];
if (error) {
NSLog(@"error: %@", error);
}
}
- (NSManagedObjectModel*)managedObjectModel
{
return [[NSManagedObjectModel alloc] initWithContentsOfURL:self.modelURL];
}
// Subscribe to NSPersistentStoreDidImportUbiquitousContentChangesNotification
- (void)persistentStoreDidImportUbiquitousContentChanges:(NSNotification*)note
{
NSLog(@"%s", __PRETTY_FUNCTION__);
NSLog(@"%@", note.userInfo.description);
NSManagedObjectContext *moc = self.managedObjectContext;
[moc performBlock:^{
[moc mergeChangesFromContextDidSaveNotification:note];
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifiCloudStoreDidChange" object:nil];
/*
// 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 {
NSLog(@"storeWillChange");
NSManagedObjectContext *moc = self.managedObjectContext;
//[moc performBlockAndWait:^{
[moc performBlock:^{
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.
}
// Subscribe to NSPersistentStoreCoordinatorStoresDidChangeNotification
- (void)storesDidChange:(NSNotification *)note {
// here is when you can refresh your UI and
// load new data from the new store
NSLog(@"storeDidChange");
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifiCloudStoreDidChange" object:nil];
}
#pragma mark - Regular Methods -
-(Book *)insertNewBookWithDate:(NSDate *)newDate
{
Book *newBook = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:self.managedObjectContext];
newBook.bookName = @"Book";
newBook.publishDate = newDate;
[self.managedObjectContext save:nil];
return newBook;
}
-(void)deleteBook:(Book *)book
{
[self.managedObjectContext deleteObject:book];
[self.managedObjectContext save:nil];
}
-(NSArray *)fetchEntityOfType:(NSString *)entityType withPredicate:(NSPredicate *)predicate andSortKey:(NSString *)sortKey
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityType inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Specify criteria for filtering which objects to fetch
[fetchRequest setPredicate:predicate];
// Specify how the fetched objects should be sorted
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey
ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil)
{
NSLog(@"couldn't fetch entity of type '%@', error: %@", entityType, error.localizedDescription);
}
return fetchedObjects;
}
@end
I struggled with a similar issue. 我遇到了类似的问题。 I would see:
我会看见:
Using local storage: 1
but no other output. 但没有其他输出。 And if I rebuilt the app I would get something like:
如果我重建了应用程序,我会得到类似的东西:
Error adding store for new account:
One thing to note is that I would only get this output if I first presses the "home button" on the iPhone, and then reopened the app. 有一点需要注意的是,如果我第一次按下iPhone上的“主页按钮”,然后重新打开应用程序,我只能获得此输出。
A key thing to note is that I had no services selected . 需要注意的一个关键事项是我没有选择任何服务 。 To fix this issue, I selected "iCloud Documents".
为了解决这个问题,我选择了“iCloud Documents”。
You may need to delete the app before rebuilding. 您可能需要在重建之前删除该应用程序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.