Работа с базами данных в iPhone, SQLite и работа с датами
Работа с базами данных в iPhone, SQLite и работа с датами
Известно что SQLite в борьбе за облегченность и компактность напрямую не поддерживает формат «Даты». Встретившись с данной проблемой при создании iPhone приложения (а SQLite это единственное СУБД для iPhone), я не нашёл ни одной достойной документации на русском языке. Что и вызвало желание написать данный пост.
Для решения данного вопроса я обратился на сайт www.sqlite.org/lang_datefunc.html
В моем случае дату я решил хранить в текстовом формате (т.е. тип данных TEXT):
CREATE TABLE regDoc(‘id’ INTEGER PRIMARY KEY AUTOINCREMENT, ‘nameDoc’ TEXT, ‘dateDoc’ TEXT)
При работе с таким подходом главное соблюдать формат представления данных (даты) на протяжение всего приложения.
SQLite поддерживает следующие функции работы с датой:
Function (Равна -)Equivalent strftime()
date(. ) — strftime(‘%Y-%m-%d’, . )
time(. ) — strftime(‘%H:%M:%S’, . )
datetime(. ) — strftime(‘%Y-%m-%d %H:%M:%S’, . )
julianday(. ) — strftime(‘%J’, . )
Использование основных функций имеет сложности с форматами даты для стран не Европы и США так как например в России дата имеет формат %d.%m.%Y, поэтому я использую аналог этих функций strftime().
Пример работы с датой в SQLite
const char *sql = «select id,nameDoc,dateDoc from regDoc where (strftime(‘%d.%m.%Y’,dateDoc) =strftime(‘%d.%m.%Y’,?)) order by strftime(‘%d.%m.%Y’,dateDoc) DESC»;
Здесь мы видим условия отбора по дате и сортировка. Дата в таблице хранится как текст.
Разберем условие
(strftime(‘%d.%m.%Y’,dateDoc) -(NSMutableArray *)selectListDocFrom:(NSString *)dateOn:(NSString *)dateFrom:(int)flagInOut <
// Подключаемся к базе данных (процедуру подключения в этом блоке не станем // описывать)
if ([self initConnect]) <
sqlite3_stmt *statement;
// Формируем запрос — знак ? Это будущий параметр выборки (его значение
// задается далее
const char *sql = «select id,nameDoc,dateDoc from regDoc where (strftime(‘%d.%m.%Y’,dateDoc) =strftime(‘%d.%m.%Y’,?)) order by strftime(‘%d.%m.%Y’,dateDoc) DESC»;
// Компилируем запрос в байткод перед отправкой в базу данных
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) <
// Производим постановку параметров в компилированный запрос
// Второй параметр в методе sqlite3_bind_text указывает в место какого по
//счету знака вопроса подставить ЗНАЧЕНИЕ !Внимание этот параметр
//начинается с 1 а не с 0
sqlite3_bind_text(statement, 1, [dateOn UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [dateFrom UTF8String], -1, SQLITE_TRANSIENT);
//Условия нужные для приложения — НЕЗАБЫВАЕМ что данные для запроса
//предоставляются в кодировке UTF8String
if (flagInOut==0)
<
sqlite3_bind_text(statement, 3, [@»+» UTF8String], -1, SQLITE_TRANSIENT);
> else if (flagInOut==1)
<
sqlite3_bind_text(statement, 3, [@»-» UTF8String], -1, SQLITE_TRANSIENT);
>
//Далее выполняем запрос
while (sqlite3_step(statement) == SQLITE_ROW) <
//Перебор результата запроса
//В переборе мы видим что доступ к результату осуществляется методом
//sqlite3_column_text или sqlite3_column_int или sqlite3_column_float
//где первый параметр есть результат запроса а второй номер поля из запроса по
//даннай строке (картеж (кто помнит институт)) НАЧИНАЕТСЯ с 0 а не с 1
[self.records addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@»%d»,sqlite3_column_int(statement, 0)],@»id», [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 1)], @»dateDoc»,[NSString stringWithFormat:@»%00.00f»,sqlite3_column_double(statement, 2)],@»summa»,[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 3)],@»typeOperation»,nil]];
>
//Если нет ошибок мы возвращаем результат и прекращаем выполнения
//цикла выборки данных
return self.records;
> else
//В случай выборки данных Сообщаем о ней и закрываем базу
> else <
//В случай формирования запроса Сообщаем о ней и закрываем базу
NSAssert1(NO, @»Failed to open database with message ‘%s’.», sqlite3_errmsg(database));
>
return nil;
Источник
Core Data для iOS. Глава №1. Теоретическая часть
Хабралюди, добрый день!
Сегодня хочу начать написание ряда лекций с практическими заданиями по книги Михаеля Привата и Роберта Варнера «Pro Core Data for iOS», которую можете купить по этой ссылке. Каждая глава будет содержать теоретическую и практическую часть.
Содержание:
- Глава №1. Приступаем (Практическая часть)
- Глава №2. Усваиваем Core Data
- Глава №3. Хранение данных: SQLite и другие варианты
- Глава №4. Создание модели данных
- Глава №5. Работаем с объектами данных
- Глава №6. Обработка результатирующих множеств
- Глава №7. Настройка производительности и используемой памяти
- Глава №8. Управление версиями и миграции
- Глава №9. Управление таблицами с использованием NSFetchedResultsController
- Глава №10. Использование Core Data в продвинутых приложениях
Приступаем
Что такое Core Data?
Используя компьютеры для выполнения своих задач, люди рассчитывают, что внесенные ими изменения будет сохранены. Сохранение изменений играет важную роль в офисных программных пакетах, текстовых редакторах, играх, браузерах и тд. Большинство программного обеспечения нуждается в возможности хранить введенные пользователем данные для последующего восстановления состояния работы, но конечно же есть и такое ПО, которое в этом не нуждается — калькуляторы, новостные ленты, будильники, виджеты о погоде.
Понимание того, каким образом можно хранить данные на iDevice, является критически важным при разработке продвинутых приложений.
Apple предоставляет гибкий фрэймворк для работы с хранимыми на устройстве данными — Core Data. Конечно же Core Data не панацея и есть другие варианты хранения данных, которые могут лучше подойти при решении определенных задач, но уж очень хорошо и красиво Core Data вписывается в Cocoa Touch. Большинство деталей по работе с хранилищем данных Core Data скрывает, позволяя вам сконцентрироваться на том, что действительно делает ваше приложение веселым, уникальным и удобным в использовании.
Не смотря на то, что Core Data может хранить данные в реляционной базе данных вроде SQLite, Core Data не является СУБД. По-правде Core Data в качестве хранилища может вообще не использовать реляционные базы данных. Core Data не является чем-то вроде Hibernate, хотя и предоставляет некоторые возможности ORM. Core Data скорее является оболочкой/фрэймворком для работы с данными, которая позволяет работать с сущностями и их связями (отношениями к другим объектами), атрибутами, в том виде, который напоминает работы с объектным графом в обычном объектно-ориентированном программировании.
Core Data был внедрён лишь начиная с Mac OS X 10.4 Tiger и iPhone SDK 3.0
История хранения данных в iOS
Как за выпущенные Pixar мультфильмы мы должны благодарить компанию NeXT, так и за Core Data мы должны сказать ей спасибо. Core Data родилась и эволюционировала из технологии, называемой Enterprise Objects Framework (EOF).
Дебют фрэймворка приходится на 2005 год с запуском Mac OS X 10.4 (Tiger), а вот в IPhone появляется лишь начиная с версии 3.0.
До того, как Core Data пришла на IPhone, разработчикам приходилось не сладко и для хранения данных могли быть использованы следующие варианты:
1) Списки свойств, которые содержали пары ключ-значение из различных типов данных.
2) Сериализация данных и хранение их в файлах (использовался протокол NSCoding)
3) Реляционная база данных SQLite
4) Хранение данных в облаке
Эти методы всё еще продолжают использоваться, хотя и ни один метод из четырёх не сравнится по удобству с использованием Core Data. Несмотря на рождение таких фрэймворком, как FMDatabase и ActiveRecord, для решения проблемы с хранением данных до появления Core Data, разработчики с удовольствием переключились на Core Data после его появления.
Хотя повторюсь, что Core Data не является лекарством от всех болезней, иногда вы конечно будете обращаться к решениям с использованием, например, списка свойств, но чаще всего вы будете сталкиваться с необходимостью и желанием использовать в своих приложениях именно Core Data, как инструмент, который наилучшим образом позволяет решить вашу проблему.
Чем чаще и чем больше вы будете программировать и использовать Core Data, тем чаще у вас будет возникать не вопрос «Стоит ли мне использовать Core Data?», а «Есть ли причина не использовать Core Data?».
Создание простого приложения с Core Data
В этой секции мы создадим простое приложение основанное на Core Data из одного из шаблонов XCode, основные части которого разберём. В конце этой части вы поймете, каким образом приложение взаимодействует с Core Data для хранения и получения данных.
Понимание основных компонентов Core Data
Перед тем, как погрузиться в код и разбор тестового приложения, необходимо иметь представление о компонентах Core Data. На рисунке ниже продемонстрированы основные элементы, которые мы будем использовать в тестовом приложении.
Как пользователь Core Data вы никогда не должны работать напрямую с хранилищем данных. Абстрагируйтесь от хранилища, от типа хранилища, думайте только о данных! Особенностью такого подхода является возможность безболезненно сменить тип хранилища (был XML файл, а стал SQLite) не меняя большое кол-ва написанного вами кода.
Объекты, которые находятся под управлением фрэймворка (Core Data) должны наследовать методы/свойства класса NSManagedObject.
Так же, как и людям, объектам нужна среда в которой они могут существовать, такая среда есть и, называется она managed object context (среда управляемых объектов) или просто context. Среда, в которой находится объект, следит не только за тем, в каком состоянии находится объект с которым вы работаете, но и за состояниями связанных объектов (объектов, которые зависимы от данного и от которых зависим он сам).
Экземпляр класса NSManagedObjectContext предоставляет ту самую среду для объектов, объект данного типа должен быть доступен в вашем приложении всегда. Обычно экземпляр класса NSManagedObjectContext является свойством делегата вашего приложения. Без среды, без экземпляра класса NSManagedObjectContext вам просто не удастся работать с Core Data.
Создание нового проекта
Запустим XCode и создадим новый проект из шаблона Master-Detail Application:
Заполним поля следующим образом:
И после завершения создания увидим примерно такую картину:
Запускаем наш проект
Перед тем как начать разбираться, что находится под капотом данного приложения, давайте запустим и разберемся, что вообще делает приложение.
Жмём на кнопку «Run» и перед нами появится вот такое:
Нажмем несколько раз на кнопку с «+» и в списке появится несколько записей со временем.
Теперь завершим (остановим) работу приложения и, если приложение не использовало бы Core Data для хранения данных, то при очередном запуске мы увидели бы пустой список снова, однако после перезапуска мы видим всё ту же картину:
Разбираем составляющие приложения
Структура приложения относительно проста. В наличие имеется модель данных, которая описывает сущность хранимую в базе данных (хранилище); контроллер, который облегчает взаимодействия между экраном (таблицей) и хранилищем данных; делегат приложения, который помогает инициализировать и запустить приложение.
На изображении представленном ниже показаны классы используемые в приложении и как они соотносятся друг с другом:
Обратите внимание на то, что класс MasterViewController содержит свойство, которое ссылается на экземпляр класса NSManagedObjectContext для взаимодействия с Core Data. Пройдясь по коду можно увидеть, что MasterViewController получает managed object context из свойства делегата приложения.
BasicApplication.xcdatamodel представляет собой директорию в файловой системе, которая содержит информацию о структуре модели данных. Модель данных является основой каждого приложения использующего Core Data.
Модель данных данного приложения описывает лишь одну сущность — Event. Сущность Event содержит лишь одно свойство (поле, атрибут) — timeStamp типа Date.
Сущность Event типа NSManagedObject, который считается основным для всех сущностей находящимся под управлением Core Data. Во второй главе мы рассмотрим тип NSManagedObject более подробно.
Извлечение/выборка данных
Следующим классом, который нас интересует, является MasterViewController. В его заголовочном файле описаны два свойства на которые мы обратим внимание:
NSFetchedResultsController представляет собой контроллер, предоставляемый фрэймворком Core Data для управления запросами к хранилищу.
NSManagedObjectContext является известной нам уже средой существования объектов типа NSManagedObject.
Реализация класса MasterViewController, которую можно найти в файле MasterViewController.m, показывает, каким образом можно взаимодействовать с Core Data для получения и хранения данных. В реализации класса MasterVIewController имеется явный геттер fetchedResultsController, который производит предварительную настройку запроса на выборку данных из хранилища.
Первым шагом к осуществлению запроса на выборку данных является создание запроса:
Результаты запроса могут быть отсортированы при помощи NSSortDescriptor. NSSortDescriptor определяет поле для сортировки и тип сортировки (по возрастанию или убыванию).
В нашем примере сортируем по убыванию времена создания записей:
После того, как запрос определен, мы можем приступить к созданию NSFetchedResultsController.
Используя в качестве делегата NSFetchedResultsController MasterVIewController мы можем следить за состоянием данных хранилища (удаление, добавление, перемещение и тд) и безболезненно интегрировать данное решение с UITableView. Мы можем конечно же получить те же результаты вызывая метод executeFetchRequest в managed object context, но в таком случае мы не получим и не сможем воспользоваться всеми преимуществами NSFetchedResultsController.
Создание и настройка переменной экземпляра класса NSFetchedResultsController:
Вы наверно заметили, что используемый ранее метод initWithFetchRequest имеет параметр cacheName. При передаче в качестве аргумента nil Вы исключаете возможность кэширования результата запроса, но при указании наименования кэша, вы позволяете Core Data проверить наличие такого же ранее осуществленного запроса и вернуть результат из кэша. В противном случае, если запроса с таким именем кэша нет, то будет осуществлен запрос к хранилищу и возвращены необходимые данные, которые будет закэшированы впоследствии.
В завершение нам осталось только выполнить запрос для получения данных:
Ниже вы можете ознакомиться с полным геттером fetchedResultsController:
NSFetchedResultsController представляет собой нечто вроде коллекции объектов типа NSManagedObject, для этого у него даже имеется свойства fetchedObjects типа NSArray для получения доступа к результатам запроса.
Класс MasterVIewController, который так же расширяет возможности UITableViewController, демонстрирует нам насколько удобно использовать NSFetchedResultsController для управления содержимым таблиц.
Вставка нового объекта
Быстро окинув взглядом метод insertNewObject: станет понятно, каким образом создаются и добавляются новые события в хранилище. NSManagedObject определяются описанием сущности из модели данных и могут существовать только в определенном контексте (среде). Первым шагом для создания нового объекта является получение контекста в котором этот объект будет создан:
Последним шагом, который необходимо осуществить, является сохранение контекста в котором был создан новый объект. Учтите, что при сохранении контекста все несохраненные ранее изменения будут сохранены.
Полный метод добавления нового объекта представлен ниже:
Инициализация контекста (среды)
Очевидно, что всё, что мы раньше делали не может быть достигнуто без создания объекта контекста, без той самой среды в которой существует и живет объект. Как раз за создание этой самой среды отвечает делегат приложения. Три свойства, которые должны быть обязательно в любом приложении использующем Core Data:
Обратите внимание на то, что все три свойства readonly, делается это для того, чтобы извне их нельзя было установить. Изучая BasicApplicationAppDelegate.m видно, что все три свойства имеют явные геттеры.
Managed Object Model:
После чего создается хранилище поддерживающее созданные модели данных. В нашем случае, собственно, как и в большинстве других с использованием Core Data, хранилище данных основывается на SQLite.
Создаём контекст (среду):
Контекст используется во всём приложении в качестве интерфейса для взаимодействия с Core Data и постоянным хранилищем.
Последовательность инициализации Core Data:
Механизм запускается при вызове метода application:didFinishLaunchingWithOptions:
Вызывая геттер свойства managedObjectContext делегата приложения на выполнение запускается цепочка действий:
— (NSManagedObjectContext *)managedObjectContext вызывает
— (NSPersistentStoreCoordinator *)persistentStoreCoordinator, который в свою очередь производит вызов
— (NSManagedObjectModel *)managedObjectModel.
Таким образом вызов геттер-метода managedObjectContext инициализирует весь стэк объектов Core Data и приводит Core Data в готовность.
Добавление Core Data в уже существующий проект
Осуществляется в три шага:
- Добавление фрэймворка Core Data
- Создание модели данных
- Инициализация контекста (среды)
Добавление фрэймворка Core Data
В мире Objective-C библиотеки называют фрэймворками.
Стандартные фрэймворки, которые уже подключены в «чистых» проектах и которые вы чаще всего увидите:
- UIKit
- Foundation
- Core Graphics
Где производится добавление новых фрэймворков:
Выбираем фрэймворк для подключения:
Создание модели данных
Ни одно приложение Core Data не может считаться завершенным без модели данных. Модель данных описывает все сущности, которые будут находиться в ведении Core Data.
Для создания новой модели данных: File -> New -> New File
Назовем нашу модель MyModel.xcdatamodeld:
После создания модели перед вами откроется окно редактирования (создания) сущностей.
Создать новую сущность можно кликнув на кнопку «+» в левой нижней части XCode, а добавить новый атрибут сущности можно нажав на кнопку «+» уже в разделе «Attributes» и затем выбрать его тип.
Инициализация контекста (среды)
Последний шаг состоит в инициализации контекста, хранилища и объектной модели. Чаще всего вы можете воспользоваться кодом, который автоматически генерируется XCode в «пустых» проектах использующих Core Data.
Переключимся в *.m файл DemoAppAppDelegate и напишем следующие строки:
Далее следует код, который производит инициализацию модели данных, хранилища и контекста.
Методы, которые ранее мы еще нигде не реализовывали:
Теперь, когда мы подключили Core Data, наше приложение может использовать его для хранения данных.
Давайте реализуем простой пример, который бы позволил нам убедиться в том, что всё работает так, как надо и данные действительно сохраняются.
В тестовом примере мы будем определять кол-во раз, которое было запущено наше приложение.
Внесём маленькие изменения в метод application:didFinishLaunchingWithOptions: делегата приложения в виде получения кол-ва раз, которое приложения запускалось ранее и добавление нового события запуска.
Код для получения предыдущих запусков приложения:
После чего мы можем пройтись по всему массиву следующим образом:
Добавим новую запись и сохраним:
После первого запуска приложение выведет в консоль следующую строку:
После второго запуска:
Полный метод выглядит следующим образом:
В завершение
Прошу об ошибках не писать в комментариях, лучше сразу в личные сообщения.
Продолжать ли переводы? Есть ли интерес к данной теме?
Источник