Pro Core Data for iOS. Глава №2. Практическая часть

от автора

Хабралюди, добрый день!
Сегодня хочу начать вольный перевод книги Михаеля Привата и Роберта Варнера «Pro Core Data for iOS», которую можете скачать по этой ссылке. Каждая глава будет содержать теоретическую и временами практическую часть.

image

Содержание:

  • Глава №1. Приступаем (Практическая часть)
  • Глава №2. Усваиваем Core Data (Практическая часть)
  • Глава №3. Хранение данных: SQLite и другие варианты
  • Глава №4. Создание модели данных
  • Глава №5. Работаем с объектами данных
  • Глава №6. Обработка результатирующих множеств
  • Глава №7. Настройка производительности и используемой памяти
  • Глава №8. Управление версиями и миграции
  • Глава №9. Управление таблицами с использованием NSFetchedResultsController
  • Глава №10. Использование Core Data в продвинутых приложениях

Вступление

Будем мы создавать вот такую модель:
image

Затем добавим несколько записей и запросим их. Вывод будет производиться в консоль, чтобы мы не задавались еще и вопросами визуальными.
Готовы? Тогда поехали!

Описание

Будем создавать объектный граф нашего любимого ресурса — Хабра.
Есть N основных объектов:

  • Запись в блоге
  • Пользователь
  • Теги
  • Хабы
  • Вопросы
  • Ответы
  • Учетная запись компании

Этого будет достаточно.

Какие данные содержит каждый из объектов?

  • Запись в блоге — заголовок, текст
  • Пользователь — ник, карма, рейтинг, пол, аватар, почтовый ящик
  • Тег — наименование
  • Хаб — наименование, профильный ли хаб
  • Вопрос — заголовок, текст
  • Ответ — текст
  • Учетная запись компании — наименование организации, рейтинг
Приступаем к работе

Открываем XCode и создаём новый проект Single View Application:

Вводим наименование проекта, префиксы и прочее:
image

Знакомое окно:
image

Добавляем Core Data

Добавляем Core Data Framework в проект:
image

Создаем модели

Добавить новый файл -> iOS -> CoreData -> Data Model
image
image

Начнём с записи в блоге.
Создаем новую сущность и называем её Blogpost, добавляем два атрибута caption (String) и text (String).
image

Создаем новую сущность и называем её User, добавляем атрибуты avatar (String), email (String), gender (Decimal), karma (Integer 16), nickname (String), rating (Integer 16).
image

Создаем новую сущность и называем её Tag, добавляем всего один атрибут name (String).
image

Создаем новую сущность и называем её Hab, добавляем два атрибута name (String), target (Boolean).
image

Создаем новую сущность и называем её Question, добавляем два атрибута caption (String), text (String).
image

Создаем новую сущность и называем её Response, добавляем один атрибут text (String).
image

Создаем новую сущность и называем её Organization, добавляем два атрибута name (String), rating (Integer 16).
image

Вот что у нас есть в итоге:
image

Теперь займемся установкой отношений между объектами.
У учетной записи компании есть множество пользователей (сотрудников).
У пользователя есть множество записей в блоге, у записи в блоге есть множество тегов и хабов.
У пользователя есть множество вопросов, а у вопроса множество ответов.

Начнем с построение связи «учетная запись компании» (один-ко-многим) «пользователи».
Выбираем из списка сущностей Organization и добавляем новую связь нажав на "+" в разделе Relationships:
image

Так как по умолчанию XCode создаёт связь один-к-одному мы должны изменить тип связи:
image

Теперь у каждой организации есть множество пользователей. Поле Inverse (обратная связь) мы сейчас тоже установим, но предварительно добавим новую связь в сущность Пользователя и назовем её organization (организация в которой работает пользователь, а если он не работает нигде, то поле будет null):
image

Теперь вновь откроем редактирование сущности Organization и установим поле Inverse в organization:
image

Вот как стал выглядеть теперь объектный граф:
image

Попробуйте расставить остальные связи сами, а затем сверьте с тем, что у меня получилось.
Вы знаете как создать связь, вы знаете как изменить тип связи с один-к-одному на один-ко-многим — этого будет достаточно.
Итоговая картина:
image

Создаем организацию

Уже имеем в AppDelegate.h такое:

// //  TMAppDelegate.h //  Habrahabr // //  Created by AndrewShmig on 8/31/13. //  Copyright (c) 2013 TM. All rights reserved. //  #import <UIKit/UIKit.h> #import <CoreData/CoreData.h>  @class TMViewController;  @interface TMAppDelegate : UIResponder <UIApplicationDelegate>  @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) TMViewController *viewController;  @property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; @property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;  @end 

И в AppDelegate.m:

// //  TMAppDelegate.m //  Habrahabr // //  Created by AndrewShmig on 8/31/13. //  Copyright (c) 2013 TM. All rights reserved. //  #import "TMAppDelegate.h"  @implementation TMAppDelegate  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     return YES; }  #pragma mark - Core Data Stack  - (NSManagedObjectModel *)managedObjectModel {     if(nil != _managedObjectModel)         return _managedObjectModel;          _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];          return _managedObjectModel; }  - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {     if(nil != _persistentStoreCoordinator)         return _persistentStoreCoordinator;          NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory                                                                inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Habrahabr.sqlite"];          NSError *error = nil;     _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];     if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);         abort();     }          return _persistentStoreCoordinator; }  - (NSManagedObjectContext *)managedObjectContext {     if(nil != _managedObjectContext)         return _managedObjectContext;          NSPersistentStoreCoordinator *store = self.persistentStoreCoordinator;     if(nil != store){         _managedObjectContext = [[NSManagedObjectContext alloc] init];         [_managedObjectContext setPersistentStoreCoordinator:store];     }          return _managedObjectContext; }  @end 

Перепишем метод application:didFinishLaunchingWithOptions: таким образом:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //    Создаем организацию     NSManagedObject *yandex = [NSEntityDescription insertNewObjectForEntityForName:@"Organization"                                                             inManagedObjectContext:self.managedObjectContext];     [yandex setValue:@"Yandex Inc." forKey:@"name"];     [yandex setValue:@672 forKey:@"rating"];      //    создаем сотрудников Yandex     NSManagedObject *pupkin = [NSEntityDescription insertNewObjectForEntityForName:@"User"                                                             inManagedObjectContext:self.managedObjectContext];     [pupkin setValue:@"VaseaPup" forKey:@"nickname"];     [pupkin setValue:@"vasilisa@yandex.ru" forKey:@"email"];     [pupkin setValue:@1 forKey:@"gender"]; // 0 - unknown, 1 - male, 2 - female     [pupkin setValue:@0 forKey:@"karma"];     [pupkin setValue:@0 forKey:@"rating"];          NSManagedObject *gosha = [NSEntityDescription insertNewObjectForEntityForName:@"User"                                                            inManagedObjectContext:self.managedObjectContext];     [gosha setValue:@"Goshka" forKey:@"nickname"];     [gosha setValue:@"gosha.k@yandex.ru" forKey:@"email"];     [gosha setValue:@0 forKey:@"gender"];     [gosha setValue:@0 forKey:@"karma"];     [gosha setValue:@0 forKey:@"rating"];      //    связываем сотрудников с компанией     NSMutableSet *employees = [yandex mutableSetValueForKey:@"employees"];     [employees addObject:pupkin];     [employees addObject:gosha];      //    сохраняем данные в хранилище     [self saveContext];          NSLog(@"Finish!");          return YES; } 

Запустим приложение. Данные должны были быть сохранены. Проверим это.
Найдем файл Habrahabr.sqlite:
image

Запустим терминал и проверим структуру БД:

AndrewShmigs-MacBook-Pro:~ new$ cd "/Users/new/Library/Application Support/iPhone Simulator/6.1/Applications/95B0716A-9C2C-4BD8-8117-62FB46BB5879" AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ ls Documents	Habrahabr.app	Library		tmp AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ cd Documents/ AndrewShmigs-MacBook-Pro:Documents new$ ls Habrahabr.sqlite AndrewShmigs-MacBook-Pro:Documents new$ sqlite3 Habrahabr.sqlite  SQLite version 3.7.12 2012-04-03 19:43:07 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> .schema CREATE TABLE ZBLOGPOST ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR ); CREATE TABLE ZHAB ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTARGET INTEGER, ZBLOGPOSTS INTEGER, ZNAME VARCHAR ); CREATE TABLE ZORGANIZATION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZRATING INTEGER, ZNAME VARCHAR ); CREATE TABLE ZQUESTION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR ); CREATE TABLE ZRESPONSE ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZQUESTION INTEGER, ZTEXT VARCHAR ); CREATE TABLE ZTAG ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZBLOGPOST INTEGER, ZNAME VARCHAR ); CREATE TABLE ZUSER ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZKARMA INTEGER, ZRATING INTEGER, ZORGANIZATION INTEGER, ZGENDER DECIMAL, ZAVATAR VARCHAR, ZEMAIL VARCHAR, ZNICKNAME VARCHAR ); CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB); CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER); CREATE INDEX ZBLOGPOST_ZAUTHOR_INDEX ON ZBLOGPOST (ZAUTHOR); CREATE INDEX ZHAB_ZBLOGPOSTS_INDEX ON ZHAB (ZBLOGPOSTS); CREATE INDEX ZQUESTION_ZAUTHOR_INDEX ON ZQUESTION (ZAUTHOR); CREATE INDEX ZRESPONSE_ZQUESTION_INDEX ON ZRESPONSE (ZQUESTION); CREATE INDEX ZTAG_ZBLOGPOST_INDEX ON ZTAG (ZBLOGPOST); CREATE INDEX ZUSER_ZORGANIZATION_INDEX ON ZUSER (ZORGANIZATION); sqlite> select * from ZORGANIZATION; 1|3|1|672|Yandex Inc. sqlite> select * from ZUSER; 1|7|1|0|0|1|0||gosha.k@yandex.ru|Goshka 2|7|1|0|0|1|1||vasilisa@yandex.ru|VaseaPup sqlite>  

Добавим теперь одному из сотрудников вопрос и создадим какой-то пост.

//    добавляем сотруднику Гоше вопрос     NSManagedObject *whoAmI = [NSEntityDescription insertNewObjectForEntityForName:@"Question"                                                             inManagedObjectContext:self.managedObjectContext];     [whoAmI setValue:@"Who am I?" forKey:@"caption"];     [whoAmI setValue:@"Помогите мне узнать себя лучше." forKey:@"text"];          NSMutableSet *goshasQuestions = [gosha mutableSetValueForKey:@"questions"];     [goshasQuestions addObject:whoAmI]; 

Запустим приложение и проверим БД:

sqlite> select * from ZQUESTION; 1|4|1|4|Who am I?|Помогите мне узнать себя лучше. sqlite>  

Добавим блогпост сотруднику Васе Пупкину:

//    добавляем сотруднику Васе Пупкину новый блогпост     NSManagedObject *newPost = [NSEntityDescription insertNewObjectForEntityForName:@"Blogpost"                                                              inManagedObjectContext:self.managedObjectContext];     [newPost setValue:@"yandex.Деньги & yandex.Карты & yandex.ДваСтвола" forKey:@"caption"];     [newPost setValue:@"Some text" forKey:@"text"];      //    добавляем два Хаба     NSManagedObject *hab1 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"                                                           inManagedObjectContext:self.managedObjectContext];     [hab1 setValue:@"iOS" forKey:@"name"];     [hab1 setValue:@YES forKey:@"target"];          NSManagedObject *hab2 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"                                                           inManagedObjectContext:self.managedObjectContext];     [hab2 setValue:@"Objective-C" forKey:@"name"];     [hab2 setValue:@YES forKey:@"target"];          NSMutableSet *habs = [newPost mutableSetValueForKey:@"habs"];     [habs addObject:hab1];     [habs addObject:hab2];          [[pupkin mutableSetValueForKey:@"blogposts"] addObject:newPost]; 

И вывод:

sqlite> select * from ZBLOGPOST; 1|1|1|5|yandex.Деньги & yandex.Карты & yandex.ДваСтвола|Some text sqlite> select * from ZHAB; 1|2|1|1|1|iOS 2|2|1|1|1|Objective-C sqlite>  
Вывод данных

Выведем всех сотрудников и наименования компаний в которых они работают. Код для чтения данных будем писать в том же методе в котором писали и запись данных.

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"User"];     NSArray *allUsers = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];          for(NSManagedObject *user in allUsers){         NSString *nickname = [user valueForKey:@"nickname"];         NSString *organization = [user valueForKeyPath:@"organization.name"];                  NSLog(@"%@ works at %@", nickname, organization);     } 

Получим примерно такой вывод (приложение у меня запускалось несколько раз и данные тоже несколько раз были добавлены):

2013-08-31 13:00:27.255 Habrahabr[18148:c07] Goshka works at Yandex Inc. 2013-08-31 13:00:27.257 Habrahabr[18148:c07] VaseaPup works at Yandex Inc. 2013-08-31 13:00:27.258 Habrahabr[18148:c07] VaseaPup works at Yandex Inc. 2013-08-31 13:00:27.258 Habrahabr[18148:c07] Goshka works at Yandex Inc. 2013-08-31 13:00:27.259 Habrahabr[18148:c07] VaseaPup works at Yandex Inc. 2013-08-31 13:00:27.259 Habrahabr[18148:c07] Goshka works at Yandex Inc. 2013-08-31 13:00:27.260 Habrahabr[18148:c07] Finish! 

Обратите внимание на то, как мы получили наименование организации в которой работает пользователь. Как Вам? А? Я так и думал, что понравится!

В завершение

Экспериментируйте! Не бойтесь, если что-то сломается, полный проект находится по этой ссылке.

Удачи и благодарю за внимание!
Надеюсь вам понравилась практическая часть.

ссылка на оригинал статьи http://habrahabr.ru/post/192090/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *