Первый урок по CoreData.
Создаем пустой проект и указываем галочку Use CoreData.
Для отслеживания (дебага) запросов к CoreData выбираем схему (левый верхний угол окна) -> edit Scheme -> Слева выбираем Run и переходим на вкладку Arguments.
Добавляем новый параметр (+ нажать) в блок Arguments Passed On Launch:
-com.apple.CoreData.SQLDebug 1
В проекте присутствует 3 новых объекта
Модель NSManagedObjectModel *managedObjectModel связана с файлом в котором хранятся описания всех сущностей и связей, которые хранятся в базе данных.
Координатор NSPersistentStoreCoordinator *persistentStoreCoordinator он осуществляет считывание и сохранение данных в файл базы данных. Он использует модель, чтобы знать какие сущности хранятся в файле базы данных.
Контекст NSManagedObjectContext *managedObjectContext это та среда которая используется в приложении для создания, сохранения объектов в базу данных. Приложение взаимодействует с базой данных через контекст. Контекст связан с координатором, который отвечает за непосредственный обмен данных с файлом базы данных.
Для создания новой сущности/модели нужно зайти в файл модели с расширением .xcdatamodeld и нажать кнопку Add Entity
Назовем новую сущность, так же как класс TAStudent
Добавим атрибуты в сущность fistName, LastName - типа String, dateBirth - типа Date, score -типа Double.
Для того, чтобы создать объект в кордате он должен быть типа NSManagedObject.
Добавляем объект студент - сущьность типа TAStudent и инициализируем его с помощью KVC:
Для того, что сделать запрос из базы данных:
Можно выдать результат в виде количества - объектов NSCountResultType, если сами объекты не нужны.
Если результат в виде массива объектов NSManagedObject, то вывести их можно так
for (NSManagedObject *object in arrayResult) {
Теперь для вывода результата можно использовать этот класс:
Добавим функцию для создания рандомного студента.
Из прошлых проектов можно взять массивы имен и фамилий студентов.
Выполняем эту функцию и сохраняем контекст:
Так же можно сделать валидацию значений для проперти, аналогично предыдущему уроку по KVC:
Создаем пустой проект и указываем галочку Use CoreData.
Для отслеживания (дебага) запросов к CoreData выбираем схему (левый верхний угол окна) -> edit Scheme -> Слева выбираем Run и переходим на вкладку Arguments.
Добавляем новый параметр (+ нажать) в блок Arguments Passed On Launch:
-com.apple.CoreData.SQLDebug 1
В проекте присутствует 3 новых объекта
Модель NSManagedObjectModel *managedObjectModel связана с файлом в котором хранятся описания всех сущностей и связей, которые хранятся в базе данных.
Координатор NSPersistentStoreCoordinator *persistentStoreCoordinator он осуществляет считывание и сохранение данных в файл базы данных. Он использует модель, чтобы знать какие сущности хранятся в файле базы данных.
Контекст NSManagedObjectContext *managedObjectContext это та среда которая используется в приложении для создания, сохранения объектов в базу данных. Приложение взаимодействует с базой данных через контекст. Контекст связан с координатором, который отвечает за непосредственный обмен данных с файлом базы данных.
Для создания новой сущности/модели нужно зайти в файл модели с расширением .xcdatamodeld и нажать кнопку Add Entity
Назовем новую сущность, так же как класс TAStudent
Добавим атрибуты в сущность fistName, LastName - типа String, dateBirth - типа Date, score -типа Double.
Для того, чтобы создать объект в кордате он должен быть типа NSManagedObject.
Добавляем объект студент - сущьность типа TAStudent и инициализируем его с помощью KVC:
NSManagedObject *student = [NSEntityDescription insertNewObjectForEntityForName:@"TAStudent" inManagedObjectContext:self.managedObjectContext];
[student setValue:@"Vasya" forKey:@"firstName"];
[student setValue:@"Pupkin" forKey:@"lastName"];
[student setValue:@3.8 forKey:@"score"];
[student setValue:[NSDate dateWithTimeIntervalSinceReferenceDate:-60*60*24*365*10] forKey:@"dateBirth"];
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(@"Error: %@", [error localizedDescription]);
}
Если приложение падает с ошибкой, то это происходит из-за того, что первоначально в базе не было сущности, а мы добавили ее позже, а файл уже создан. Из-за этого происходит конфликт.
Самый простой способ это удалить приложение Simulator -> Hardware -> Home.
Можно добавить строчку, чтобы при конфликте старый файл удалялся. В функцию - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
После условия
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
Добавляем
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *description = [NSEntityDescription entityForName:@"TAStudent" inManagedObjectContext:_managedObjectContext];
[request setEntity:description];
NSError *requestError = nil;
NSArray *arrayResult = [self.managedObjectContext executeFetchRequest:request error:&requestError];
if (requestError) {
NSLog(@"Request Error: %@", [requestError localizedDescription]);
}
else{
NSLog(@"Result %@", arrayResult);
}
Можно выбрать тип результата для запроса, например результат в виде дикшионари для удобного отображения:
[request setResultType:NSDictionaryResultType];
Можно выдать результат в виде количества - объектов NSCountResultType, если сами объекты не нужны.
Если результат в виде массива объектов NSManagedObject, то вывести их можно так
for (NSManagedObject *object in arrayResult) {
NSLog(@"%@ %@ = %@", [object valueForKey:@"firstName"], [object valueForKey:@"lastName"], [object valueForKey:@"score"]);
}
Для удобства работы с сущностями мы можем создать специальный класс наследник NSManagedObject, для этого добавляем в проект файл, слева выбираем Core Data, справа NSManagedObject subclass, выбираем файл с моделью и нашу сущность на основе нее будет создан новый класс.Теперь для вывода результата можно использовать этот класс:
for (TAStudent *student in arrayResult) {
NSLog(@"%@ %@ = %@", student.firstName, student.lastName, student.score);
}
Из прошлых проектов можно взять массивы имен и фамилий студентов.
static NSString* firstNames[] = {
@"Tran", @"Lenore", @"Bud", @"Fredda", @"Katrice",
@"Clyde", @"Hildegard", @"Vernell", @"Nellie", @"Rupert",
@"Billie", @"Tamica", @"Crystle", @"Kandi", @"Caridad",
@"Vanetta", @"Taylor", @"Pinkie", @"Ben", @"Rosanna",
@"Eufemia", @"Britteny", @"Ramon", @"Jacque", @"Telma",
@"Colton", @"Monte", @"Pam", @"Tracy", @"Tresa",
@"Willard", @"Mireille", @"Roma", @"Elise", @"Trang",
@"Ty", @"Pierre", @"Floyd", @"Savanna", @"Arvilla",
@"Whitney", @"Denver", @"Norbert", @"Meghan", @"Tandra",
@"Jenise", @"Brent", @"Elenor", @"Sha", @"Jessie"
};
static NSString* lastNames[] = {
@"Farrah", @"Laviolette", @"Heal", @"Sechrest", @"Roots",
@"Homan", @"Starns", @"Oldham", @"Yocum", @"Mancia",
@"Prill", @"Lush", @"Piedra", @"Castenada", @"Warnock",
@"Vanderlinden", @"Simms", @"Gilroy", @"Brann", @"Bodden",
@"Lenz", @"Gildersleeve", @"Wimbish", @"Bello", @"Beachy",
@"Jurado", @"William", @"Beaupre", @"Dyal", @"Doiron",
@"Plourde", @"Bator", @"Krause", @"Odriscoll", @"Corby",
@"Waltman", @"Michaud", @"Kobayashi", @"Sherrick", @"Woolfolk",
@"Holladay", @"Hornback", @"Moler", @"Bowles", @"Libbey",
@"Spano", @"Folson", @"Arguelles", @"Burke", @"Rook"
};
- (TAStudent *)addRandomStudent{
TAStudent *student = [NSEntityDescription insertNewObjectForEntityForName:@"TAStudent" inManagedObjectContext:_managedObjectContext];
student.score = @(arc4random_uniform(201)/100.f +2.f);
student.dateBirth = [NSDate dateWithTimeIntervalSince1970:arc4random_uniform(20)*60*60*24*365];
student.lastName = lastNames[arc4random_uniform(50)];
student.firstName = firstNames[arc4random_uniform(50)];
return student;
}
Выполняем эту функцию и сохраняем контекст:
[self addRandomStudent];
[self.managedObjectContext save:nil];
Каждый объект имеет ссылку на контекст, поэтому если мы находимся в другом классе (где нет self.managedObjectContext можно вызвать сохранение контекста так:
TAStudent *student = [self addRandomStudent];
[student.managedObjectContext save:nil];
Для переопределения сеттера и геттера в классе наследнике NSManagedObject, необходимо сообщить обсерверам CoreData об изменении проперти для сеттера или про получения доступа для геттера:
- (void)setFirstName:(NSString *)firstName
{
[self willChangeValueForKey:@"firstName"];
[self setPrimitiveValue:firstName forKey:@"firstName"];
[self didChangeValueForKey:@"firstName"];
NSLog(@"SET FIRST NAME");
}
- (NSString*)firstName
{
NSString *string = nil;
[self willAccessValueForKey:@"firstName"];
string = [self primitiveValueForKey:@"firstName"];
[self didAccessValueForKey:@"firstName"];
NSLog(@"Get FIRST NAME");
return string;
}
- (BOOL) validateValue:(id _Nullable __autoreleasing *)value forKey:(NSString *)key error:(NSError * _Nullable __autoreleasing *)error
{
if ([key isEqualToString:@"lastName"]) {
*error = [NSError errorWithDomain:@"BAD LAST NAME" code:123 userInfo:nil];
return NO;
}
return YES;
}
Можно определить так же ф-ю конкретно для определенного ключа вида:
- (BOOL) validateLastName:(id _Nullable __autoreleasing *)value error:(NSError * _Nullable __autoreleasing *)error
Если функция валидации возвращает NO, то при сохранении контекста возникнет эта ошибка и объект не будет сохранен.
Исходный код урока.
Исходный код урока.
Домашнее задание по всей CoreData будет в конце 44 урока.