Кто-нибудь получил настройку синхронизации данных ядра 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 до ее работы?
Супер путать @_ @