【问题标题】:Xcode 6 iOS 8 iCloud core data setupXcode 6 iOS 8 iCloud 核心数据设置
【发布时间】: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


【解决方案1】:

iOS 8 解决方案:

好的……然后……哈哈。我想我解决了。

新发现。浏览此页面后:

http://www.tuaw.com/2014/09/17/psa-do-not-upgrade-to-icloud-drive-during-ios-8-installation/

上面写着:

iCloud Drive 是 Apple 全新改进的 iCloud 同步和文件 允许您在 iOS 8 之间共享文档的存储功能 设备和运行 OS X 10 Yosemite 的 Mac。

所以,我决定硬着头皮把我的 iCloud 帐户升级到 iCloud 驱动器(免费升级)。

升级到 iCloud 驱动器后,重新运行我的应用,对 Xcode 6 进行了一些更改,它现在可以工作了。

需要注意的一些重要事项:

  • iCloud Drive 与以前的 iCloud 文档和数据存储不兼容。因此,如果您要进行测试,请确保您的所有设备都使用 iCloud 驱动器和 iOS 8。
  • 模拟器似乎只同步一次,在启动应用程序后,而设备在每个间隔连续同步。不确定它是否是模拟器错误。或者我的配置并不完美。
  • 第一次尝试使用“使用默认容器”对我来说在模拟器中不起作用(但在设备上它确实起作用),可能需要删除应用程序的先前副本并重新安装。首先尝试使用默认容器,看看它是否有效,否则,请阅读下面的下一点。
  • 由于上述原因,我改为使用具有这种模式的 Ubiquity 容器:

    iCloud.$(CFBundleIdentifier)

比如:

iCloud.com.xxxxxxxx.iCloudCoreDataDemo

其中“xxxxxxxx”是我的公司名称标识符。

我通过登录我的 iOS 开发者中心制作了上面的 iCloud 容器,也许你可以在 Xcode 6 中按“+”号并在那里输入一个,Xcode 应该会自动为你设置一切。

我用来测试它是否工作的一个代码块是这样的:

- (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;
}

如果你看到“欢呼?”那么没关系,您还应该在 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

注意两条重要的线:

Using local storage: 1

后来变成:

Using local storage: 0

本地存储 1 表示它当前正在使用本地存储,而本地存储 0 表示它已将数据移动到 iCloud 存储。

我希望这对其他人有所帮助。

仅 iOS 7 解决方案:

好的,所以我刚刚发现了一些东西并设法让它仅适用于 iOS 7。我还没有弄清楚如何在 iOS 8 中做到这一点,但我注意到了一些重要的事情。

在运行 iOS 8.0.2 的 iPhone 5 上,iCloud 设置菜单中不再有“Document & Data”选项。

但是,在我运行 iOS 7 的 iPad 上,我确实看到了“文档和数据”选项。

也许这就是它在 iOS 8 上无法运行的原因,我们不再有文档和数据存储?

无论如何,这是我发现的仅适用于 iOS 7 的解决方案。

我在这里找到了这个页面

https://developer.apple.com/library/ios/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html

其中一行说:

  • iCloud 文档存储适用于用户可见的基于文件的内容、Core Data 存储或其他基于文件的复杂内容。

果然,我进入了我的 Xcode 6 项目文件并勾选了“iCloud Documents”选项。这使单选按钮未变灰,但我仍将其保留在“使用默认容器”。

我学到的一件事是我需要在 appDelegate 中初始化我的 PersistentStack。以前,我尝试在 +(id)sharedInstance 方法中初始化持久堆栈,但它导致 iCloud 仅在第一次同步,所以在初始加载和同步后,添加新记录之后不会同步。

我重写了一个基本的应用程序并稍微修改了持久堆栈:

应用代理.h

#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

App Delegate.m

@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"];
}

持久堆栈.h

#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

持久堆栈.m

#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

【讨论】:

  • -URLForUbiquityContainerIdentifier 的文档:重要不要从应用程序的主线程调用此方法。由于此方法可能需要大量时间来设置 iCloud 并返回请求的 URL,因此您应该始终从辅助线程调用它。要确定 iCloud 是否可用,尤其是在启动时,请检查 ubiquityIdentityToken 属性的值。
  • 因此,如果您有混合使用已转换为 iCloud Drive 和未转换为 iCloud 的用户,那么单个应用程序是否能够处理这两种情况?
  • 我对@EmilyJ 没有一个可靠的答案,但从它看来,Apple 正在强迫所有人使用新的 iCloud Drive。不知道老方法能不能和新方法共存。机会看起来很渺茫。我认为至少,您必须维护一些 if-else 检查,以查看用户使用的是否低于 iOS 8(iOS 5、iOS 6、iOS 7)与高于或等于 iOS 8。跨度>
  • 你在 iOS 8 上是对的,需要使用 iCloud 驱动器才能使 CoreData 工作。以前 (iOS 7) 可以使用 developer.icloud.com 查看 CoreData 事务文件。但是使用 iCloud Drive,容器将不再显示在该站点中。您知道其他可以查看这些 CoreData 活动的网站吗?
  • @EmilyJ 您是否注意到列中带有 iCloud 选项卡的调试导航器(运行期间)。它为 iCloud 存储和传输提供数据。
【解决方案2】:

我也遇到过类似的问题。我会看到:

Using local storage: 1

但没有其他输出。如果我重建应用程序,我会得到类似的东西:

Error adding store for new account:

需要注意的是,如果我首先按下 iPhone 上的“主页按钮”,然后重新打开应用程序,我只会得到这个输出。

需要注意的关键是我没有选择任何服务。为了解决这个问题,我选择了“iCloud Documents”。

您可能需要在重建之前删除应用程序。

【讨论】:

    猜你喜欢
    • 2014-11-12
    • 2018-01-08
    • 1970-01-01
    • 1970-01-01
    • 2013-11-20
    • 1970-01-01
    • 2016-11-02
    • 2014-11-09
    • 1970-01-01
    相关资源
    最近更新 更多