ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

17. Базы данных

SQLite

Учитывая количество просьб этой статьи я думаю, что нет смысла объяснять что такое SQLite и для чего он нужен… Поэтому сразу приступим к работе.

 

В качестве примера я предлагаю написать что-то на подобие записной книжки. Все данные программа будет хранить в базе SQLite. Сразу хочу обратить внимание, что для создания подобного рода программ не стоит использовать такой мощный механизм как базы данных. Достаточно обойтись *.plist файлом или хранить записи в NSUserDefaults. То есть, проект, который мы сейчас создадим будет создан только в учебных целях.

 

Поскольку наша программа будет работать с базой данных — эту базу следует создать. У этой задачи есть как минимум два решения. Первое — это воспользоваться консолью и создать базу с помощью соответственных комманд консоли. Второй — интерактивный, с помощью FireFox и плагина для него SQLite Manager for FireFox. Поскольку с консолью не все дружат — я покажу второй способ.

 

Для начала откроем сам плагин:

 

Затем нажимаем на кнопку создания новой базы данных

 

 

И указываем имя новой базы (я решил назвать notes_db)

 

 

Укажите место хранения базы (можете выбрать любое, на данном этапе это не важно) и после этого нажмине на кнопку создания новой таблицы:

 

 

Заполните поля как на картинке ниже и нажмите на кнопку ОК

 

 

Плагин создаст SQL-запрос при выполнении которого в базу будет добавлена таблица с указанными полями.

 

Теперь создадим непосредственно проект с шаблона (Master-Detail Application), который будет использовать нашу базу данных. Свой проект я назвал Notes. Теперь наш прокт следует подготовить к работе с базой данных. Для этого добавим в проект три вещи.

 

  • файл базы данных;
  • фрэймверк libsqlite3.0;
  • класс SQLiteAccess (скачать).

 

Дело в том, что я не хочу учить вас как изобретать велосипед (в большинстве случаев это и не нужно). Я предлагаю воспользоваться оберткой (готовым классом для работы с SQLite).

 

Примечание: Класс SQLiteAccess не поддерживает ARC. Это следует учитывать при добавлении его в проект с поддержкой автоматического подсчета ссылок и добавить соответствующий флаг компилятора -fno-objc-arc. Подробнее об этом можна прочитать в уроке Automatic Reference Counting (ARC).

 

Теперь перейдем к программированию. В первую очередь внесем изменения в интерфейс класса MasterViewController:

 

MasterViewController.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;
#import "SQLiteAccess.h"

@class DetailViewController;

@interface MasterViewController : UITableViewController

@property (strong, nonatomic) DetailViewController *detailViewController;

- (NSArray*)notes;

@end</code>

 

Большую часть работы за нас проделал Xcode, поместив в шаблон все необходимое для работы с таблицей. Я лишь импортировал класс SQLiteAccess и добавил метод — (NSArray*)notes, который будет делать запрос к базе и возвращать массив записей. Теперь реализуем получение данных с базы и вывод их в таблицу:

 

MasterViewController.m

<code data-result="[object Object]">#import "MasterViewController.h"

#import "DetailViewController.h"

@implementation MasterViewController

@synthesize detailViewController = _detailViewController;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] 
                                  initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                                  target:self 
                                  action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.tableView reloadData];
}

- (NSArray*)notes {
    return [SQLiteAccess selectManyRowsWithSQL:@"select * from notes"];
}

- (void)insertNewObject:(id)sender
{
    [SQLiteAccess updateWithSQL:@"insert into notes (note) values ('iMaladec')"];
    [self.tableView reloadData];
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.notes.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    NSDictionary *note = [self.notes objectAtIndex:indexPath.row];

    cell.textLabel.text = [note objectForKey:@"note"];
    cell.detailTextLabel.text = [[note objectForKey:@"date"] description];

    return cell;
}

- (void)tableView:(UITableView *)tableView 
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 
forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
    	NSDictionary *note = [self.notes objectAtIndex:indexPath.row];

        NSString *noteID = [note objectForKey:@"id"];
        NSString *queryString = [NSString stringWithFormat:@"delete from notes where id = '%@'", 
                                 noteID];
        [SQLiteAccess deleteWithSQL:queryString];

        [self.tableView reloadData];
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (!self.detailViewController) {
        self.detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" 
                                                                           bundle:nil];
    }

    NSDictionary *note = [self.notes objectAtIndex:indexPath.row];
    self.detailViewController.detailItem = note;
    [self.navigationController pushViewController:self.detailViewController animated:YES];
}

@end</code>

 

В шаблоне был объявлен массив NSMutableArray *_objects;, который я удалил. Его нам заменил вышеупомянутый метод — (NSArray*)notes, он возвращает список записей полученный с базы. Кроме этого, я изменил метод — (void)insertNewObject:(id)sender, вызываемый при нажатии на кнопку плюс (она была добавлена шаблоном Xcode). Теперь этот метод просто добавляет запись в базу и перегружает таблицу. Так же, перезагрузку таблици я вызываю в методе методе viewWillAppear. Это нужно для того, чтобы отобразить актуальные записи после их изменения в DetailViewController (а нем чуть позже). Сам метод viewWillAppear срабатывает каждый раз при переходе на текущий контроллер. Хочу обратить ваше внимание на метод cellForRowAtIndexPath и то, каким образом отображаются данные в ячейках. Дело в том, что класс SQLiteAccess возвращает нам не просто массив записей, а массив словарей (NSDictionary). Имена ключей в этих словарях совпадают с именами полей таблици. В методе редактирования таблици, для типа редактирования UITableViewCellEditingStyleDelete (удаление) я добавил код, который удаляет запись с базы и снова перегружает таблицу.

 

Для работы нашей программы осталось указать имя базы, с которой приложение будет работать. Для этого перейдите в файл SQLiteAccess.h и там вы увидите строку #define dbName @»name_db». Замените значение это дефайна на имя базы, которое вы указывали при ее создании (notes_db).

 

Если вы запустите приложение — оно будет работать, но таблица не отобразит ни одной записи. Дело в том, что наша база пустая. Добавить записи вы сможете кнопкой с плюсиком или с помощью плагина, которым мы создавали базу и проверить работоспособность приложения. Осталось реализовать лишь редактирование записей. Для этого Xcode и добавил в шаблон класс DetailViewController, но как и MasterViewController он нуждается в изменении. Вы наверняка обратили внимание на метод didSelectRowAtIndexPath в котором осуществляется переход к редактору записей и передача текущей выбранной записи. Теперь изменим класс DetailViewController:

 

Теперь изменим только что добавленный класс.

DetailViewController.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;
#import "SQLiteAccess.h"

@interface DetailViewController : UIViewController

@property (strong, nonatomic) NSDictionary *detailItem;

@property (strong, nonatomic) IBOutlet UITextView *detailDescription;

@end</code>

 

Как и в случае с MasterViewController, мы просто импортировали SQLiteAccess. Затем изменили тип detailItem с id на NSDictionaryи detailDescriptionLabel изменили на detailDescription, а его тип UILabel на UITextView. (не забудьте внести соответствующие изменения вDetailViewController.xib, в противном случае — приложение будет падать)

 

DetailViewController.m

 

<code data-result="[object Object]">#import "DetailViewController.h"

@interface DetailViewController ()
- (void)configureView;
@end

@implementation DetailViewController

@synthesize detailItem = _detailItem;
@synthesize detailDescription = _detailDescription;

#pragma mark - Managing the detail item

- (void)setDetailItem:(id)newDetailItem
{
    if (_detailItem != newDetailItem) {
        _detailItem = newDetailItem;

        [self configureView];
    }
}

- (void)configureView
{
    if (self.detailItem) {
        self.detailDescription.text = [self.detailItem objectForKey:@"note"];
    }
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.detailDescription = nil;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem *done =[[UIBarButtonItem alloc] 
                            initWithBarButtonSystemItem:UIBarButtonSystemItemDone
                            target:self
                            action:@selector(pressDone)];
    self.navigationItem.rightBarButtonItem = done;

    [self configureView];
}

- (void)pressDone {
    NSString *noteID = [self.detailItem objectForKey:@"id"];
    NSString *queryString = [NSString stringWithFormat:@"update notes set note = '%@' WHERE id='%@'", 
                             self.detailDescription.text, noteID];

    [SQLiteAccess updateWithSQL:queryString];
    [self.navigationController popViewControllerAnimated:YES];	
}

@end</code>

 

Основные изменения, касающиеся реализации класса DetailViewController заключаются в добавлении кнопки Done в методе viewDidLoadи написании кода обработки нажатия на эту кнопку. В pressDone мы прсото сохраняем запись в базу и возвращаемся к MasterViewController.

 

Вот и все, наша записная книга с использованием базы SQLite готова.

Comments are closed.