Vývoj aplikací pro iPhone: použití SqLite v iPhone
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
1 |
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:
1 2 3 4 |
CREATE TABLE contacts( id INTEGER PRIMARY KEY, name VARCHAR(100), surname VARCHAR(100)); |
Stejně tak si můžeme do tabulky rovnou přidat nějaký záznam, aby nebyla prázdná:
1 |
INSERT INTO contacts VALUES('1', 'Jiri', 'Zachar'); |
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
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:
1 2 3 4 5 6 7 8 |
#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ě:
1 2 |
//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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//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í:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//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:
1 2 3 4 5 6 |
@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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//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.
Chtěl jsem si pořídit MAC Mini abych se mohl začít učit vývoj APP pro iOS, ale když vidím tu šílenou syntax tak si to asi ještě rozmyslím.
Je kvalitní alespoň IDE pro vývoj, ulehčuje práci programátorovi tím, že našeptává metody, doplňuje syntaxu a správně upozorňuje na možné chyby atd..
Děkuji
IDE je docela fajn, i kdyz me, jako uzivateli Windows docela dlouho trvalo, nez jsem si na nej zvyknul prave kvuli iOS.
Intellisense v xCode funguje vcelku spolehlive a programovani urcite hodne ulehcuje. Celej xCode je vcelku pouzitelny IDE, obsahuje temer vsechno co potrebuju, od jednoduchyho debugovani aplikaci az po simulovani na iPhone, nebo treba na iPadu.
Interface Builder pro vytvoreni rozhrani je taky vcelku pouzitelny.
"Nakonec je to asi jediná možnost jak uchovávvat v aplikaci velké množství dat."
A co CoreData?
CoreData je framework, ktery praci s daty usnadnuje, pouziva sice i jene uloziste, ale stejne tak i sqlite.
CoreData je mnohem vic nez je framework nad sqlite a urcite nez pouzivat ciste sqlite, je vhodnejsi pouzit neco jako FMDB nebo naky jiny framework, ktery pracuje nad sqlite
Ano, presne to jsem napsal, CoreData umoznuje praci s ruznymy datovymi ulozisti, nicmene, tenhle clanek je pouze o sqlite a jeho zakladnim pouziti.