http://www.zaachi.com/cs/items/vyvoj-aplikaci-pro-iphone-pouziti-sqlite-v-iphone.html

Vývoj aplikací pro iPhone: použití SqLite v iPhone

Publikováno: 11.01.2011 13:23:15

Kam ukládat data v iPhone? Použití SqLite databáze v iPhone za využití jazyka Cocoa.

Pokud budete vyvíjet aplikace pro iPhone určitě narazíte na probém, kam s daty, které není možné uchovávavat v paměti aplikace.

Tohle je ideální případ pro využítí databáze. Samozřejmě iPhone umí s databází pracovat. Konkrétně používá SqLite .

Přidání databáze do iPhone

Jako prvni je nutne databazi vytvořit a přidat si ji do prostředí iPhone (do xCode).

Na iOS si spustíme si terminál a ideálně přejdeme do adresáře s projektem. Tady zadáme příkaz

sqlite3 data.sqlite

čímž se nám vytvoří soubor data.sqlite, ve kterém bude uložena naše databáze.

Nyní máte otevřenou konzoli pro sqlite, takže nám nic nebrání si rovnou vytvořit nějakou tabulku, například tabulku s kontakty.
Napíšeme:

CREATE TABLE contacts(
id INTEGER PRIMARY KEY, 
name VARCHAR(100), 
surname VARCHAR(100));

 

iphone sql

 

Stejně tak si můžeme do tabulky rovnou přidat nějaký záznam, aby nebyla prázdná:

INSERT INTO contacts VALUES('1', 'Jiri', 'Zachar');

 

iphone sql

 

Konzoli můžeme zavřít a přídáme si databází do našeho projektu. Stací pouhým přetažením do Resources, nebo přes Add - Exists file .

Tím databáze v projektu bude existovat a bude možné ji používat.

Přídání knihoven do projektu

Pro přístup k databázi budeme potřebovat přidat do projektu knihovnu pro sqlite3. To uděláme jednoduše pravým kliknutím na Frameworks a výběrem: Add - Existing Frameworks.
V nabídce vybereme libsqlite3.0.dylb

 

iphone sql

 

Dotaz na databázi

V současné době jsme ve stavu, kdy máme v projektu přidanou databázi a přidány správné knihovny, takže můžete vytvořit třídu, pro přístup k databázi.

Do projektu si přídáme novou třídu ( Objective C class ), čímž se nám vytvoří jeden .m a jeden .h soubor. Pojmenovat je můžeme například MySqlite .

Jako první si upravíme soubor MySqlite.h , tedy interface třídy, které bude dědit NSObject a prozatím nebude obsah žádnou metodu.

Navíc do hlavičkového souboru přidáme include sqlite:

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface MySqlite : NSObject {
	
}

@end

A můžeme se přepnout do implementace (do m. souboru) ve kterém si vytvoříme tří metody.

Metoda Popis
- (sqlite3 *) getDBConnection bude vracek konexi k databázi
- (sqlite3_stmt *)makeSQL:(char *)sql provede sql dotaz a vratí statement
- (void)createEditableCopyOfDatabaseIfNeeded metoda zkontroluje, zda je soubor s databází editovatelný, případně tento stav ošetří.

Třetí metoda createEditableCopyOfDatabaseIfNeeded, není nijak extra důležitá, databáze bude většinou editovatelná, minimálně při simulování, ale nastat může.

Nejprve se přepněte se za řádek, kde začíná implementace třídy a vytvoříme si proměnnou s názvem souboru s databází, abychom jej nemuseli definovat znova v každé metodě:

//soubor s databazi
NSString *dbFileName = @"data.sqlite";

A začneme rovnou s impmentací metody createEditableCopyOfDatabaseIfNeeded, kterou budeme volat před přístupem k databázi.

 

Metoda bude fungovat tak, že vytvoří novou instaci NSFileManageru, načte soubor s dabází a zjistí, jestli je možné do něj zapisovat. V případě že ne, pokusí se jej zkopírovat na jíné misto, aby k němu měla aplikace přístup:

//kontroluje, zda je mozne soubor s dabazi editovat, pokud ne, zkopiruje jej
- (void)createEditableCopyOfDatabaseIfNeeded {
	//vytvoří novy file manager 
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSError *error;
	//writableDBPath bude obsahovat nacteny soubor s databazi
	NSString *writableDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
								stringByAppendingPathComponent:dbFileName];

	//pokud existuje soubor s moznosti zapisu, neprodadime dalsi akci
	if ([fileManager fileExistsAtPath:writableDBPath]){
		return;
	}
	
	//Pokud neexistuje editovatelny soubor s databazi, zkopirujeme jej.
	NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbFileName];
	//znova kontrolumeme jestli je mozne soubor editovat
	if (! [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error] ) {
		NSLog(@"Databazi neni mozne editovat");
	}
}

Máme ošetřený případ, že soubor s databází nejde otevřít, takže můžeme navázat konexi k databázi. Metoda getDBConnection načte soubor s databází a vrátí připojení:

//vraci konexi k databazi
- (sqlite3 *) getDBConnection{
	//vytvori editovatelnou verzi databaze, pokud je potreba
	[self createEditableCopyOfDatabaseIfNeeded];
	//promenna typu sqlite3
	sqlite3 *DBConnection;
	//vytvorime novy path pro soubor data.sqlite
	NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] 
					  stringByAppendingPathComponent:dbFileName];
	
	//otevreme spojeni s databazi
	if( sqlite3_open([path UTF8String], &DBConnection) != SQLITE_OK) {
		//v pripade ze se nepovedlo pripojit, tak vypiseme chybu
		NSLog(@"Databazi se nepodarilo otevrit");
		return FALSE;
	}
	
	//vracime novou db connection
	return DBConnection;
}

Jako poslední nám zbývá metoda pro provedení sql dotazu. Metoda bude samozřejmě obsažena v interface, takže si upravíme interface v hlavičkovém souboru:

@interface MySqlite : NSObject {
	
}

- (sqlite3_stmt *)makeSQL:(char *)sql;
@end

Funkce makeSQL bude volat getDBConnection, která ji vrátí připojení k databázi a vykoná sql dotaz, který bude jako vstupní parametr metody:

//funkce pro zpracovani sql dotazu
- (sqlite3_stmt *)makeSQL:(char *)sql{
	NSLog(@"Provadim dotaz: %s", sql);

	//prazdny statement
	sqlite3_stmt *statement = nil;
	
	//volame pripojeni k databazi
	sqlite3 *db = [self getDBConnection];
	//pokud bylo pripojeni navazano
	if ( db ) {
		//pokusime se vykonat sql dotaz
		if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) != SQLITE_OK) {
			NSLog(@"Chyba v dotazu: %s", sqlite3_errmsg(db));
		}
	}
	else {
		NSLog(@"Chyba v pripojeni k db");
	}

	//vracime statement DB
	return statement;
}

Návratová hodnota metody je sqlite3_stmt, se kterým můžeme dále pracovat.

Ještě jednou si můžeme uvést celý kód m souboru:

#import "MySqlite.h"

@implementation MySqlite

//soubor s databazi
NSString *dbFileName = @"data.sqlite";

//kontroluje, zda je mozne soubor s dabazi editovat, pokud ne, zkopiruje jej
- (void)createEditableCopyOfDatabaseIfNeeded {
	//vytvoří novy file manager 
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSError *error;
	//writableDBPath bude obsahovat nacteny soubor s databazi
	NSString *writableDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
								stringByAppendingPathComponent:dbFileName];

	//pokud existuje soubor s moznosti zapisu, neprodadime dalsi akci
	if ([fileManager fileExistsAtPath:writableDBPath]){
		return;
	}
	
	//Pokud neexistuje editovatelny soubor s databazi, zkopirujeme jej.
	NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbFileName];
	//znova kontrolumeme jestli je mozne soubor editovat
	if (! [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error] ) {
		NSLog(@"Databazi neni mozne editovat");
	}
}

//vraci konexi k databazi
- (sqlite3 *) getDBConnection{
	//vytvori editovatelnou verzi databaze, pokud je potreba
	[self createEditableCopyOfDatabaseIfNeeded];
	//promenna typu sqlite3
	sqlite3 *DBConnection;
	//vytvorime novy path pro soubor data.sqlite
	NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] 
					  stringByAppendingPathComponent:dbFileName];
	
	//otevreme spojeni s databazi
	if( sqlite3_open([path UTF8String], &DBConnection) != SQLITE_OK) {
		//v pripade ze se nepovedlo pripojit, tak vypiseme chybu
		NSLog(@"Databazi se nepodarilo otevrit");
		return FALSE;
	}
	
	//vracime novou db connection
	return DBConnection;
}

//funkce pro zpracovani sql dotazu
- (sqlite3_stmt *)makeSQL:(char *)sql{
	NSLog(@"Provadim dotaz: %s", sql);

	//prazdny statement
	sqlite3_stmt *statement = nil;
	
	//volame pripojeni k databazi
	sqlite3 *db = [self getDBConnection];
	//pokud bylo pripojeni navazano
	if ( db ) {
		//pokusime se vykonat sql dotaz
		if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) != SQLITE_OK) {
			NSLog(@"Chyba v dotazu: %s", sqlite3_errmsg(db));
		}
	}
	else {
		NSLog(@"Chyba v pripojeni k db");
	}

	//vracime statement DB
	return statement;
}
@end

Použití v kódu

Použití v kódu je nyní jednoduché, vytvoříme si instanci třídy MySqlite a voláme metodu makeSql, která nám vrátí výsledek dotazu, můžeme si napsat funkci (void) LoadContacts , která nám vrátí všechny kontakty z tabulky contacts :

//nacte kontakty z tabulky contacts
- (void) LoadContacts{
	//nova instance tridy MySqlite
	MySqlite *sqlConnect = [[MySqlite alloc] init];
	//volani dotazu a vraceni vysledku
	sqlite3_stmt *statement = [sqlConnect makeSQL:"SELECT * FROM contacts"];
	
	//pokud existuje vysledek
	if (statement) {
		//projdeme v cylku vsechny vysledky
		while (sqlite3_step(statement) == SQLITE_ROW) {
			//data zapiseme do logu
			NSLog(@"%@", [NSString stringWithFormat:@"%s", (char *)sqlite3_column_text(statement, 0)]);
			NSLog(@"%@", [NSString stringWithFormat:@"%s", (char *)sqlite3_column_text(statement, 1)]);
			NSLog(@"%@", [NSString stringWithFormat:@"%s", (char *)sqlite3_column_text(statement, 2)]);
		}
	}
}

Závěrem

Práce s databází v iPhone se možná zdá na první pohled složitá, ale opak je pravdou. Nakonec je to asi jediná možnost jak uchovávvat v aplikaci velké množství dat.