SQLite Tutorial per Iphone, accesso e recupero dati.. con Onomastici di Fabio Lecca
by Stefano LoduSiamo arrivati al secondo articolo di SQLite Tutorial gentilmente offerto da Fabio Lecca e del suo software Onomastici. Nel prossimo articolo conosceremo più da vicino Fabio con un intervista esclusiva per iPhone and Go. Ma per ora ascoltiamo e impariamo cos’ha da dirci su come accedere ai dati in SQLite e come recuperarli.
Accesso in lettura e scrittura
La prima funzione di SQLite che si utilizza è quella per l’apertura di un database.
Utilizziamo il metodo della classe Cocoa NSBundle per accedere al valore del percorso della bundle principale della nostra applicazione, dove XCode copia il nostro database al momento della creazione dell’applicazione:
NSString *path = [[NSBundle mainBundle] pathForResource:@”santi” ofType:@”sql”];
A questo punto, dobbiamo trasformare percorso nell’oggetto path di tipo NSString in una stringa C con codifica UTF8, in quanto come già affermato, le API di SQLite sono in linguaggio C:
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
// apertura file effettuata con successo
}
else
{
// errore!
}
L’accesso a questo file è soltanto in lettura: per effettuare modifiche al data base, è necessario effettuare la copia fisica del file del database nella cartella “Documents” della sandbox dell’applicazione. Il percorso di tale cartella si ottiene mediante la macro NSSearchPathForDirectoriesInDomains. Un’interessante conseguenza del fatto di copiare i file di lavoro in questa cartella è che iTunes effettuerà automaticamente il backup dell’applicazione e di tutti i documenti lì presenti.
Il metodo createEditableCopyOfDatabaseIfNeeded, presente nell’esempio SQLiteBooks, realizza la funzionalità di copia e viene generalmente inserito nella classe del delegato dell’applicazione, dove viene invocato nel seguente modo:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
….
[self createEditableCopyOfDatabaseIfNeeded];
….
}
all’interno del metodo applicationDidFinishLaunching, che viene invocato nelle fasi iniziali dell’apertura dell’applicazione.
Ecco il metodo riportato integralmente:
// Creates a writable copy of the bundled default database in the application Documents directory.
- (void)createEditableCopyOfDatabaseIfNeeded
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"santi.sql"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
[fileManager removeItemAtPath:writableDBPath error:&error];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”santi.sql”];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, @”Failed to create writable database file with message ‘%@’.”, [error localizedDescription]);
NSLog(@”Failed to create writable database file with message ‘%@’.”, [error localizedDescription]);
}
}
Notare che se il file è già esistente, non si effettua alcuna operazione e si risparmia tempo all’avvio dell’applicazione.
Recupero dati
Vediamo ora come effettuare una query SQL e di mostrare i risultati nell’interfaccia di iPhone.
Nella pagina principale di Onomastici viene invocato un metodo per leggere il nome dei santi del giorno e restituirlo sotto forma di oggetto NSString; vediamo la sua implementazione con alcuni commenti:
NSString *path = [[NSBundle mainBundle] pathForResource:@”santi” ofType:@”sql”];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, “SELECT santo1 FROM santi where day=? and month=?”, -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_int(statement, 1, giorno);
sqlite3_bind_int(statement, 2, mese);
if (sqlite3_step(statement) == SQLITE_ROW)
{
char *ciccio = (char *)sqlite3_column_text(statement, 0);
if (ciccio!=nil)
retval = [NSString stringWithUTF8String:ciccio];
else
retval = @”";
} else
{
retval = @”No title”;
}
}
else
{
NSLog(@”DB: query KO”);
return @”";
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
Ecco le dovute osservazioni:
1) path contiene il percorso alla versione “read-only” del database in quanto effettuiamo solamente comandi di lettura dati (SELECT)
2) la funzione sqlite3_prepare_v2 predispone un “prepared statement” allo scopo di eseguire più volte la stessa query, in cui di volta in volta inseriremo i parametri mediante la funzione sqlite3_bind_int
3) la funzione sqlite3_step(statement) restituisce SQLITE_ROW se sono stati trovati dei dati, solo in tale caso posso utilizzare la funzione sqlite3_column_text per recuperare i valori corrispondenti
4) la funzione sqlite3_column_text restituisce una stringa C, mentre nel nostro programma costruiamo una NSString mediante il comando:
[NSString stringWithUTF8String:ciccio];
tale oggetto NSString verrà passato agli opportuni metodi delle classi Cocoa UILabel che disegneranno l’interfaccia dell’applicazione
5) sqlite3_finalize e sqlite3_close vanno sempre chiamate nella sequenza corretta, allo scopo di liberare le risorse utilizzate dalla libreria
6) è sempre opportuno verificare tutti i codici di ritorno di ciascuna funzione, qualora il risultato sia diverso da SQLITE_OK occorre intraprendere l’azione correttiva adatta al caso specifico.
Per gli user di iPhone and Go… Fabio Lecca.
Sir. Lodux !





7 Comments. Subscribe to this post comments or trackback.
febbraio 21st, 2009
Non funziona.
Mi da 8 errori, eppure ho fatto copia/incolla.
febbraio 21st, 2009
ciao alberto prova ora c’era un errore alla seconda riga per via di un copy/paste che ha inserito degli “amp”
febbraio 22nd, 2009
Ah ecco.
Infatti non capivo cosa significavano quegli amp.
Comunque ci sono ancora un sacco di amp anche nel codice del metodo createEditableCopyOfDatabaseIfNeeded.
febbraio 22nd, 2009
Alberto, ho fatto tutte le correzioni … e scusate.. per l’inconveniente.
aprile 21st, 2009
Io aggiungendo il file sqlite (anche quello del tutorial Apple Book) alle resources ottengo l’apertura del DB corretta ma poi quando effettuo la query mi restituisce l’errore in quanto non trova la tabella all’interno del DB. Però la tabella c’è e riesco ad effettuare l’interrogazione ad es. con Sqlite Manager.
Ho aggiunto anche il framework sqlite3.
L’errore riportato in consolle è DB Error: 1 “no such table: book”
Non riesco a trovare una soluzione per la semplice interrogazione del DB!
aprile 21st, 2009
Ho verificato che attualmente il Simulatore effettua una copia del mio DB ma lo salva vuoto, e non con i dati contenuti al suo interno, in quanto parto con un database già popolato…
agosto 18th, 2009
Non ho capito quale parte è per il file .h e quale per il file .m
Scusa, ma sono nuovo della programmazione per iPhone.