Pull to refresh

Core Data в iOS 5: как загрузить и импортировать существующие данные

Reading time8 min
Views14K
Original author: Adam Burkepile
Здравствуй Хабр. Я предлагаю вам перевод статьи, с помощью которой мне стали очевидны основные возможности Core Data.

Примечание:
Член команды обучающей программы iOS Adam Burkepile любезно обновил ряд Core Data в iOS 5. — В этом посту я немного расскажу об этом.



Это руководство было полностью переписано, чтобы показать более изящный способ предварительно загрузить данные, используя только Core Date вашего iPhone.

Перед началом загрузите проект.

Предварительная загрузка / импорт существующих данных

Сначала мы предварительно загружаем данные в Core Data. Есть два популярных решения этой проблемы:

1. Заполните Core Date при запуске из внешнего источника. Приложение может заметить, что база данных еще не была импортирована, и начинает читать данных из внешнего источника (такого как база данных SQLite или XML-файл) и затем начнут вставлять данные в Core Data.

2. Обеспечить предварительно заполненную SQLite базу данных. Для этого мы позволим базовым данным(Core Data) создать для нас структуру базы данных основанную на модели, и затем мы заполним нашу базу данных какой нибуть служебной программой. Служебной программой может быть наш Mac или другая программа на iPhone, которая использует Core Data, для того чтобы заполнить базу данных через Core Data API, или некоторую другую программу, которая заполняет базу данных SQLite непосредственно. Как только база данных заполнится, включите ее вмести с приложением и заставьте приложение использовать ее в качестве базы данных по умолчанию, если уже не существует какой нибуть другой базы данных.

Далее я покажу вам, как можно сделать простое служебное приложение, которое будет создавать предварительно загруженную Core Data базу данных, которую можно использовать в своем приложении.

Start

Основным преимуществом метода, который мы собираемся использовать, является то, что Core Data на iOS являются теми же самыми Core Data на OS X, и они могут использовать те же самые модели и классы.

Это означает, что мы можем написать простое консольное приложение на OS X, связать это приложения с хранилищем данных Core Data, а потом с его помощью перемещать данные из хранилища к данным нашего проекта на iOS. Ни замечательно ли это?!

Давайте проверим это. Для этого создадим консольное приложение на Mac, чтобы предварительно загрузить наши данные. Откройте Xcode и создайте новый проект, и выберите Mac OS X\Application\Command Line Tool template.


Введите “CoreDataTutorial2” как имя проекта, измените тип на “Core Data” и чтобы был включен “Use Automatic Reference Counting”.


Закончите создавать проект, затем выберите “CoreDataTutorial2.xcdatamodeld” и удалите это. Выберите “Move to Trash”, когда это спросят.


Затем войдите в каталог из архива, который скачалы, и найдите следующие файлы:

— FailedBankCD.xcdatamodeld
— FailedBankInfo.h
— FailedBankInfo.m
— FailedBankDetails.h
— FailedBankDetails.m

Скопируйте эти файлы в каталог этого проекта, и затем перетащите их в Xcode.



Проверти что включен флажок “Copy items into destination group’s folder (if needed)” если нет, то убедитесь, что проверили в «Add to target” флажок для “CoreDataTutorial2”.


Выберите main.m. Вы заметите, что мы выбрали Core Data как базовый тип данных. Теперь нужно сделать некоторые изменения, чтобы использовать наши модели классов из проекта iOS и генерировать объекты Core Data.

В managedObjectModel() замените метод:
NSString *path = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0];
path = [path stringByDeletingPathExtension];

на
NSString *path = @"FailedBankCD";

Это указывает на приложение FailedBankCD.xdatamodeld, который мы добавили вместо CoreDataTutorial2.xdatamodeld, который мы удалили в начале.

Скомпилируйте и выполните, и проверьте, что у вас нет никаких ошибок.

Если вы создавали и выполняли это ранее, вы получите ошибку несоответствия модели.
Чтобы удалить sqlite db, выберите опцию(alt) и перейдите в меню и выберите “Products” и “Clean Build Folder…”.

Если вы видите ошибку как эта:
NSInvalidArgumentException', reason: 'Cannot create an NSPersistentStoreCoordinator 
with a nil model'

То это потому, что код ищет файл ‘momd’ (файл где есть нужная версия Core Data),
но если ваша модель не будет иметь нужную версию, то это будет сохранено как простая 'mom'. Как эта статья объясняет, самая быстрая фиксация должна заменить строку в managedObjectModel () так:

 NSURL *modelURL = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"mom"]];


Теперь это должно скомпилировать и хорошо работать (без вывода все же).

Импорт данных

Для нашего примера мы будем импортировать данные из файла JSON. В ваших приложениях можно импортировать данные из различных форматов,
но центральная идея, показанная здесь (использует приложение Mac, чтобы считать файл и загрузить их в Core Data), будет все еще применяться.

Давайте испытаем это! Щелкните правой кнопкой по проекту и выберите “New File”. Выберите категорию “Other” и “Empty”.


Назовите файл “Banks.json” и удостоверьтесь, что вы включали файл в target “CoreDataTutorial2”.



В новый файл вставьте следующее:
[{ "name": "Bank1", "city": "City1", "state": "State1", "zip": 11111, "closeDate": "1/1/11" },
 { "name": "Bank2", "city": "City2", "state": "State2", "zip": 22222, "closeDate": "2/2/12" },
 { "name": "Bank3", "city": "City3", "state": "State3", "zip": 33333, "closeDate": "3/3/13" },
 { "name": "Bank4", "city": "City4", "state": "State4", "zip": 44444, "closeDate": "4/4/14" } ]


Это — JSON закодированная строка, содержащая 4 словаря в массиве. У каждого словаря есть пара свойств, которые соответствуют свойствам, которые мы имеем в наших объектах FailedBankInfo/FailedBankDetails.

Если вы не понимаете как работает JSON, проверьте это руководство.

Далее, мы должны сказать приложению копировать этот файл в продукты dictory. Выберите файл проекта, затем target “CoreDataTutorial2”. Выберите вкладку “Build Phases”,
щелкните, “Add Build Phase”, и выберите, “Add Copy Files”. Измените destintion на “Products Directory”. Наконец, перетащите файл “Banks.json” в раздел “Add files”.


Теперь у нас есть приложение, которое запускается, инициализирует Core Data, используя нашу модель FailedBank, и есть классы которые имеют источник данных с файлом Banks.json. Теперь мы имеем:

— Загрузочный файл JSON
— Преобразование JSON в массив Objective C
— Цикл через массив, который создает управляемый объект для каждого элемента
— Сохранение объектов в Core Data!

Получим кодирование! Откройте main.m и добавьте следующий код к основной функции (в конце блока autoreleasepool):
  NSError* err = nil;
    NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"Banks" ofType:@"json"];
    NSArray* Banks = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                     options:kNilOptions
                                                       error:&err];
    NSLog(@"Imported Banks: %@", Banks);


Таким образом, ваша основная функция должна быть похожей теперь на это:
int main(int argc, const char * argv[])
{
 
    @autoreleasepool {
        // Create the managed object context
        NSManagedObjectContext *context = managedObjectContext();
 
        // Custom code here...
        // Save the managed object context
        NSError *error = nil;
        if (![context save:&error]) {
            NSLog(@"Error while saving %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
            exit(1);
        }
 
        NSError* err = nil;
        NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"Banks" ofType:@"json"];
        NSArray* Banks = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                         options:kNilOptions
                                                           error:&err];
        NSLog(@"Imported Banks: %@", Banks);
 
    }
    return 0;
}


Теперь используется новый встроенный API NSJSONSerialization, чтобы легко преобразовать документ JSON в базовые типы такие как NSArray,
NSDictionary, и т.д. Чтобы узнать больше, прочтите это руководство

Дайте запустим приложение:
2012-04-14 22:01:34.995 CoreDataTutorial2[18388:403] Imported Banks: (
        {
        city = City1;
        closeDate = "1/1/11";
        name = Bank1;
        state = State1;
        zip = 11111;
    },
        {
        city = City2;
        closeDate = "2/2/12";
        name = Bank2;
        state = State2;
        zip = 22222;
    },
        {
        city = City3;
        closeDate = "3/3/13";
        name = Bank3;
        state = State3;
        zip = 33333;
    },
        {
        city = City4;
        closeDate = "4/4/14";
        name = Bank4;
        state = State4;
        zip = 44444;
    }
)


Таким образом, мы видем, что у нас теперь есть данные в объектах Objective C,
и что мы можем легко ими управлять. Теперь мы можем легко импортировать эти объекты в CoreData.

Во-первых, добавьте некоторые файлы, в вверх файла:
#import "FailedBankInfo.h" 
#import "FailedBankDetails.h"

Затем добавьте следующее ниже кода, который вы только добавили ранее:

[Banks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    FailedBankInfo *failedBankInfo = [NSEntityDescription
                                      insertNewObjectForEntityForName:@"FailedBankInfo"
                                      inManagedObjectContext:context];
    failedBankInfo.name = [obj objectForKey:@"name"];
    failedBankInfo.city = [obj objectForKey:@"city"];
    failedBankInfo.state = [obj objectForKey:@"state"];
    FailedBankDetails *failedBankDetails = [NSEntityDescription
                                            insertNewObjectForEntityForName:@"FailedBankDetails"
                                            inManagedObjectContext:context];
    failedBankDetails.closeDate = [NSDate dateWithString:[obj objectForKey:@"closeDate"]];
    failedBankDetails.updateDate = [NSDate date];
    failedBankDetails.zip = [obj objectForKey:@"zip"];
    failedBankDetails.info = failedBankInfo;
    failedBankInfo.details = failedBankDetails;
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }
}];
 
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FailedBankInfo"
                                          inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (FailedBankInfo *info in fetchedObjects) {
    NSLog(@"Name: %@", info.name);
    FailedBankDetails *details = info.details;
    NSLog(@"Zip: %@", details.zip);
}


Мы используем метод enumerateObjectsUsingBlock:, чтобы циклично выполниться через массив банков и ввести один банк и сохранить контекст. Затем мы выпускаем запрос выборки и перечисляем все банки.
2012-04-14 22:15:44.149 CoreDataTutorial2[18484:403] Name: Bank1
2012-04-14 22:15:44.150 CoreDataTutorial2[18484:403] Zip: 11111
2012-04-14 22:15:44.150 CoreDataTutorial2[18484:403] Name: Bank2
2012-04-14 22:15:44.151 CoreDataTutorial2[18484:403] Zip: 22222
2012-04-14 22:15:44.152 CoreDataTutorial2[18484:403] Name: Bank3
2012-04-14 22:15:44.152 CoreDataTutorial2[18484:403] Zip: 33333
2012-04-14 22:15:44.153 CoreDataTutorial2[18484:403] Name: Bank4
2012-04-14 22:15:44.153 CoreDataTutorial2[18484:403] Zip: 44444

Ta-da! Это — ваши данные в Core Date. Захватывающая часть — вы, можете полностью настроить способ, которым входят данные. Вместо маленького статического json файла,
который мы использовали, вы могли использовать много пары larger/a json файлов, xml файл, экспортировать файл электронной таблицы как CSV файл. Возможности действительно бесконечны.

“Doctor, you’re needed in Xcode”

Теперь мы делаем хирургию головного мозга. Мы должны взять sqlite базу данных, которую мы только-что генерировали в консоли OS X applicaiton, и пересадить это в приложение iPhone.
Самым легким способом найти sqlite базу данных является щелчок правой кнопкой по продукту CoreDataTutorial2 и избранному “Show in Finder”.


Теперь откроется новое окно Finder, где был разработан проект. Здесь, можно видеть четыре файла:

— Banks.json – json файл с информацией о банке
— CoreDataTutorial2 – Это приложение
— FailedBankCD.momd (или только.mom) – это — скомпилированная версия Managed Object Model (FailedBankCD.xdatamodeld)
— CoreDataTutorial2.sqlite – sqlite БД, которая сгенерирована приложением.


Этот файл “CoreDataTutorial2.sqlite” является juicey битом, в котором мы нуждаемся. Скопируйте этот файл в каталог проекта из учебного руководства 1.


Перетащите файл “CoreDataTutorial2.sqlite” с Finder в проект Xcode (“Copy items into destination group’s folder (if needed)” НЕ проверяется, но “Add to targets”, проверяется на “FailedBanksCD”).



Наконец откройте “FBCDAppDelegate.m”. Прокрутите вниз к методу persistentStoreCoordinator. Прямо под
NSURL *storeURL = [[[[self app... line, add:


if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]]) {
    NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"CoreDataTutorial2" ofType:@"sqlite"]];
    NSError* err = nil;
 
    if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err]) {
        NSLog(@"Oops, could copy preloaded data");
    }
}


Это проверка кода, чтобы видеть, существовала ли sqlite БД уже для этого приложения. Если ее не существует, то ищется путь для предварительно загруженного sqlite БД,
которую мы загрузили и пытаемся скопировать БД в путь для нормального дБ приложения. IT, ЧТО ПРОСТОЙ! Запустите. Должно быть что-то вроде этого:


На этом все. Всем спасибо.
Tags:
Hubs:
Total votes 13: ↑9 and ↓4+5
Comments6

Articles