简体   繁体   English

Xcode 6 iOS 8 iCloud核心数据设置

[英]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切换时,它似乎只显示键值和文档存储。

Background Info 背景资料

  • New iPad app 新的iPad应用程序
  • Xcode 6 Xcode 6
  • Targeting minimum version iOS 7 but hoping it works for iOS 8 too? 针对iOS 7的最低版本,但也希望它适用于iOS 8? (We can set iOS 8 as minimum) (我们可以将iOS 8设置为最低限度)
  • Want to use iCloud Core Data storage instead of key-value or document storage. 想要使用iCloud核心数据存储而不是键值或文档存储。
  • Have logged into the same Apple account in the Settings > iCloud for both Simulator and iPad device 已在设置> iCloud中为模拟器和iPad设备登录了相同的Apple帐户
  • My provisioning profile used to code sign the app has iCloud enabled for both development and distribution (was automatically enabled by Xcode) 用于对应用程序进行代码签名的配置文件已启用iCloud进行开发和分发(由Xcode自动启用)

My Setup 我的设置

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“服务”列表显示旁边没有刻度:

  • Key-value storage 键值存储
  • iCloud documents iCloud文档
  • CloudKit CloudKit

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: 在“服务”正下方的“容器”中,它显示以下灰色选项:

  • Use default container (this one ticked by default) 使用默认容器(默认情况下勾选此选项)
  • Specify custom containers 指定自定义容器
  • iCloud.com.xxxxxxxxxx.xxxxxxxxx (again, substituted the real identifiers with xxxx) iCloud.com.xxxxxxxxxx.xxxxxxxxx(再次用xxxx替换真实标识符)

I can't choose any option, it seems to force me to "Use default container". 我无法选择任何选项,它似乎迫使我“使用默认容器”。

Finally, Xcode seems to show ticks for: 最后,Xcode似乎显示为:

  • Add the "iCloud" entitlement to your App ID 将“iCloud”权利添加到您的App ID
  • Add the "iCloud containers" entitlement to your App ID 将“iCloud容器”权利添加到您的App ID
  • Add the "iCloud" entitlemen to your entitlements file 将“iCloud”权利添加到您的权利文件中
  • Link CloudKit.framework 链接CloudKit.framework

So by Xcode's own automated process, it setup everything for me. 因此,通过Xcode自己的自动化流程,它为我设置了一切。

The Reference Code 参考代码

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: 我已经采取了必要的代码并尝试适应我的核心数据管理器单例:

DataManager.h file 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 file 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;
}

My Observations 我的观察

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. 我真的可以使用一些帮助。

Extra Questions 额外的问题

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 @_@ 超级迷茫@ _ @

iOS 8 Solution: iOS 8解决方案:

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: 需要注意的一些重要事项:

  • iCloud Drive is incompatible with previous iCloud Document & Data storage. iCloud Drive与之前的iCloud Document&Data存储不兼容。 So if you're going to test, make sure all your devices are using iCloud drive and iOS 8. 因此,如果您要测试,请确保您的所有设备都使用iCloud驱动器和iOS 8。
  • Simulator only seems to sync once, after launching app while device continuously syncs every interval. 模拟器似乎只是同步一次,启动应用程序后设备持续同步每个间隔。 Not sure if it's a simulator bug or not. 不确定它是否是模拟器错误。 Or maybe my configuration is not perfect. 或许我的配置并不完美。
  • Using "Use default containers" doesn't work in the simulator for me (but on the device it does work) on the first try, maybe need to delete the previous copy of the app and reinstall. 在第一次尝试时,使用“使用默认容器”在我的模拟器中不起作用(但在设备上它确实有效),可能需要删除应用程序的先前副本并重新安装。 Try using default containers first and see if it works, otherwise, read the next point below. 首先尝试使用默认容器,看看它是否有效,否则,请阅读下面的下一点。
  • 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. 我希望这对其他人都有好处。

iOS 7 Only Solution: iOS 7 Only解决方案:

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: 其中一条线说:

  • iCloud document storage is for user-visible file-based content, Core Data storage , or for other complex file-based content. iCloud文档存储用于基于用户可见文件的内容, Core Data存储或其他基于文件的复杂内容。

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: 我重写了一个基本应用程序并稍微修改了持久堆栈:

App Delegate.h App Delegate.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 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"];
}

Persistent Stack.h 持久Stack.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

Persistent Stack.m 持久Stack.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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM