Подтвердить что ты не робот

Настройка данных ядра Xcode 6 iOS 8 iCloud

Кто-нибудь получил настройку синхронизации данных ядра iCloud на Xcode 6 и iOS 8? (надеюсь, это не дубликат)

Где была включена опция хранения iCloud Core Data?

Я помню, что у Core Data был дополнительный параметр хранения, называемый хранилищем Core Data, но теперь в Xcode 6 он, похоже, отображает значение ключа и хранилище документов, когда я включаю iCloud в Xcode 6.

Фоновая информация

  • Новое приложение для iPad.
  • Xcode 6
  • Ориентация минимальной версии iOS 7, но надеясь, что она работает и для iOS 8? (Мы можем установить iOS 8 как минимум)
  • Хотите использовать хранилище данных iCloud Core вместо хранилища ключей или документов.
  • Войдите в учетную запись Apple в настройках > iCloud для устройства Simulator и iPad.
  • Мой профиль обеспечения, используемый для обозначения кода приложения, включен iCloud для разработки и распространения (был автоматически включен Xcode)

Моя настройка

До сих пор я не знаю, правильно ли настроил Core Data iCloud.

Xcode, похоже, настроил контейнеры iCloud в портале разработчиков iOS:

iCloud.com.xxxxxx.xxxxxxxx   (note: I've replaced the actual strings with xxxx here)

В моем списке "сервисов" iCloud "Xcode 6" нет тиков рядом с:

  • Хранилище с ключом
  • Документы iCloud
  • CloudKit

Какой из них мы должны использовать сейчас, так как он не перечисляет "основные данные" в качестве параметра хранения?

В "Контейнерах" непосредственно под "службами" отображаются следующие параметры greyed out:

  • Использовать контейнер по умолчанию (этот по умолчанию отмечен)
  • Укажите пользовательские контейнеры
  • iCloud.com.xxxxxxxxxx.xxxxxxxxx(опять же, заменили реальные идентификаторы на xxxx)

Я не могу выбрать какой-либо вариант, он, кажется, заставляет меня использовать "Использовать контейнер по умолчанию".

Наконец, Xcode, похоже, показывает тики для:

  • Добавить право "iCloud" на ваш идентификатор приложения
  • Добавить права на iCloud-контейнеры на ваш идентификатор приложения
  • Добавьте подписчиков "iCloud" в ваш файл прав.
  • Ссылка CloudKit.framework

Таким образом, благодаря собственному автоматизированному процессу Xcode, он настраивает все для меня.

Код ссылки

Хорошо, поэтому я читаю и замечаю, что стек iCloud написан здесь:

https://github.com/mluisbrown/iCloudCoreDataStack

Я взял необходимый код и попытался адаптироваться к моему синтаксису Core Data:

Файл 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 Documents directory.
        // An iCloud enabled store will be created below a directory called CoreDataUbiquitySupport
        // in your App 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 Simulator (я считаю, это симулятор iOS 8) и на iPad-устройстве под управлением iOS 7.x

Я создал альбом с именем пользователя, введенным на симуляторе, но я не вижу устройство 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 до ее работы?

Супер путать @_ @

4b9b3361

Ответ 1

Решение iOS 8:

OK... тогда.... лол. Я думаю, что решил.

Новое открытие. После прокрутки этой страницы:

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

В нем говорится:

iCloud Drive - это новая и улучшенная iCloud синхронизация и файл Apple который позволяет вам обмениваться документами между вашим iOS 8 устройств и вашего Mac, работающего под управлением OS X 10 Yosemite.

Итак, я решил укусить пулю и обновить свою учетную запись iCloud до диска iCloud (бесплатно для обновления).

После перехода на iCloud drive и повторного запуска моего приложения с несколькими изменениями Xcode 6, он теперь работает.

Некоторые важные замечания:

  • iCloud Drive несовместим с предыдущим документом iCloud и хранилищем данных. Поэтому, если вы собираетесь протестировать, убедитесь, что все ваши устройства используют iCloud drive и iOS 8.
  • Симулятор только синхронизируется один раз, после запуска приложения, пока устройство непрерывно синхронизирует каждый интервал. Не уверен, что это ошибка симулятора или нет. Или, может быть, моя конфигурация не идеальна.
  • Использование "Использовать контейнеры по умолчанию" не работает в симуляторе для меня (но на устройстве это работает) с первой попытки, возможно, потребуется удалить предыдущую копию приложения и переустановить. Сначала попробуйте использовать контейнеры по умолчанию и посмотрите, работает ли он, иначе прочитайте следующий пункт ниже.
  • По этой причине я перешел на использование контейнера Ubiquity с этим шаблоном:

    ICloud. $(CFBundleIdentifier) ​​

Так что-то вроде:

iCloud.com.xxxxxxxx.iCloudCoreDataDemo

Где "xxxxxxxx" - это мой идентификатор имени компании.

Я сделал вышеупомянутый контейнер iCloud, войдя в мой центр разработчиков iOS, возможно, вы просто можете нажать знак "+" внутри 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, но я заметил что-то важное.

На моем iPhone 5, работающем под управлением iOS 8.0.2, у меня больше нет опции Документ и данные "в меню настроек iCloud.

Однако на моем iPad под управлением iOS 7 я вижу опции "Документ и данные".

Возможно, именно поэтому он не работает на iOS 8, у нас больше нет хранилища документов и данных?

Во всяком случае, вот что я обнаружил для решения только для iOS 7.

Я нашел эту страницу здесь

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

и одна из строк говорит:

  • Хранилище документов iCloud для видимого пользователем содержимого на основе файлов, Хранилище основных данных или для другого сложного содержимого на основе файлов.

Конечно, я вошел в файл проекта Xcode 6 и пометил "Документы iCloud". Это не было серых кнопок радио, но я все равно оставил его в "Использовать контейнеры по умолчанию".

Одна вещь, которую я узнал, это то, что мне нужно запустить мой PersistentStack в appDelegate. Раньше я пытался инициализировать постоянный стек внутри метода + (id) sharedInstance, но он заставил iCloud только синхронизировать первый раз, поэтому после начальной загрузки и синхронизации добавление новой записи впоследствии не синхронизируется.

Я переписал базовое приложение и немного изменил стойку:

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

@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

#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

Постоянный 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 Documents directory.
    // An iCloud enabled store will be created below a directory called CoreDataUbiquitySupport
    // in your App 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

Ответ 2

Я боролся с подобной проблемой. Я бы увидел:

Using local storage: 1

но другого выхода нет. И если я перестроил приложение, я бы получил что-то вроде:

Error adding store for new account:

Следует отметить, что я получаю этот результат только в том случае, если сначала нажимаю кнопку "домой" на iPhone, а затем снова открываю приложение.

Следует отметить, что у меня не было выбранных служб. Чтобы устранить эту проблему, я выбрал "Документы iCloud".

Вам может потребоваться удалить приложение перед восстановлением.