[iOs] Introduzione a SQLite e integrazione in un XCode Project

SQLITE è un Relational DBMS embedded, ovvero è disponibile sotto forma di libreria che viene inclusa in una applicazione. Non vi è alcun database che gira in background e tutte le operazioni che devono essere effettuate su tale database vengono lanciate dall’applicazione sotto forma di chiamate a funzioni contenute nella libreria SQLite.
SQLite è scritto in linguaggio C e occorre utilizzare SQLite in un progetto XCode direttamente con chiamate a funzioni in C per accedere alle strutture dati.

C’è da dire che come DBMS non è eccezionalmente performante. Già il fatto che materializza le strutture dati su un singolo file memorizzato localmente all’applicazione, fa capire che può essere impiegato in contesti in cui non occorre avere il massimo delle performance (accessi in concorrenza, grosse quantità di dati, join tra più tabelle, …). Eppure nei contesti mobile, dove comunque le strutture dati non sono molto complesse, SQLite risulta una soluzione molto versatile e comoda.

Per informazioni dettagliate su SQLite riferirsi al sito ufficiale: www.sqlite.org. Di seguito, viene fatta una panoramica veloce sugli aspetti introduttivi e sull’integrazione di questo RDBMS in un progetto XCode.

Structured Query Language (SQL)
Si accede ai dati di un database in SQLite usando il linguaggio ad alto livello conosciuto come Structured Query Language (SQL), conforme allo standard SQL-92.

Se vi interessa creare un database SQLite potete consultare le due sezioni spiegate nel resto di questo wiki: Utilizzare SQLite su MacOS X (in cui si descrive la creazione di un database e la sua gestione da Terminale) oppure Creazione di un database SQLite con SQLite Manager (add-ons di Filefox)

Utilizzare SQLite su MacOS X
MacOs X ha già SQLite preinstallato e include un ambiente interattivo per utilizzare i comandi SQL direttamente sul terminale.
Per lanciare una sessione interattiva occorre aprire il Terminale, settando la directory di lavoro (percorso su file system in cui salvare il file .db – detto anche database file) con i seguenti comandi:

sqlite3 ./mydatbase.db
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>

Dopo il prompt sqlite è possibile inserire vari comandi per creare le tabelle e inserire o recuperare i dati. Per esempio (creazione della tabella contacts):

create table contacts (id integer primary key autoincrement, name text, address text, phone text);

Per elencare tutte le tabelle presenti nel database selezionato utilizzare lo statement .tables:

sqlite> .tables
contacts

Per inserire i record nel database selezionato, basta lanciare i comandi SQL di INSERT:

sqlite> insert into contacts (name, address, phone) values ("Bill Smith", "123 main Street, California", "123-555-2323");
sqlite> insert into contacts (name, address, phone) values ("Mike Parks", "10 Upping Street, Idaho", "444-444-1212");

Per recuperare i dati basta una SELECT:

sqlite> select * from contacts;
1|Bill Smith|123 main Street, California|123-555-2323
2|Mike Parks|10 Upping Street, Idaho|444-444-1212
To extract a row that meets specific criteria:
sqlite> select * from contacts where name="Mike Parks";
2|Mike Parks|10 Upping Street, Idaho|444-444-1212

Per uscire dall’ambiente interattivo sqllite3, ecco il comando:

sqlite> .exit

Quando si lancia una app Iphone nel Simulatore o direttamente sul device, qualsiasi file database viene creato nel file system locale del simulatore stesso. Questo ha il vantaggio di avere una navigazione locale che non necessita dell’installazione manuale del database stesso. Occorre poi semplicemente arrivare al path dove è stato copiato il file del database per eseguire poi su di esso (grazie al driver sqlite) le operazioni di lettura scrittura sulle tabelle.

Creazione di un database SQLite con SQLite Manager (Add-ons di Firefox)
Se volete gestire il vostro database con una interfaccia grafica e non da terminale, come visto precedentemente, vi consiglio un add-ons di Firefox: SQLite Manager. Questo strumento vi consente di amministrare i vostri database SQLite e le tabelle, direttamente con una interfaccia grafica. Potete poi esportare il database su un file che potete comodamente importare nei vostri progetti.

E’ inutile che stia qui a spiegarne il funzionamento, perché è un plugin davvero molto semplice da usare. Se avete familiarità con strumenti di browsing di altri DBMS come MySql, questo manager vi risulterà davvero immediato.

Integrare SQLite in XCode Project
Di default, l’ambiente XCode non include SQLite nel progetto. Occorre, dunque, includere manualmente la libreria dinamica libsqlite3.dylib (cliccando sul progetto basta spostarsi sulla scheda Build Phases  e aggiungere la libreria in Link Binary with Libraries).

Successivamente, occorre importare nei file di progetto dove occorre avere un riferimento a SQLite, la dichiarazione seguente:
#import “/usr/include/sqlite3.h”

Per una spiegazione più dettagliata ed un esempio di implementazione di un progetto con l’uso di SQLite fare riferimento al seguente wiki: An Example SQLite based iPhone Application

Funzioni SQLite
Quando si implementa un database usando SQLite è necessario utilizzare un certo numero di funzioni C contenute nella libreria libsqlite3.dylib. Quelle più comuni sono le seguenti:

  • sqlite3_open() – Opens specified database file. If the database file does not already exist, it is created.
  • sqlite3_close() – Closes a previously opened database file.
  • sqlite3_prepare_v2() – Prepares a SQL statement ready for execution.
  • sqlite3_step() – Executes a SQL statement previously prepared by the sqlite3_prepare_v2() function.
  • sqlite3_column_<type>() – Returns a data field from the results of a SQL retrieval operation where <type> is replaced by the data type of the data to be extracted (text, blob, bytes, int, int16 etc).
  • sqlite3_finalize() – Deletes a previously prepared SQL statement from memory.
  • sqlite3_exec() – Combines the functionality of sqlite3_prepare_v2(), sqlite3_step() and sqlite3_finalize() into a single function call.

La lista completa delle funzioni la trovate qui: http://www.sqlite.org/c3ref/funclist.html

Dichiarazione di un Database SQLite
Prima di poter eseguire qualsiasi operazione su un database, occorre che venga dichiarato. Per fare questo occorre dichiarare una variabile che punti all’istanza della struttura di tipo sqlite3 (la struttura sqlite3 è definita nel file sqlite3.h). Per esempio:

sqlite3 *contactDB; //Declare a pointer to sqlite database structure

Apertura e creazione di un database
Una volta dichiarato, il database file può essere aperto usando la funzione sqlite3_open(). Se il database file specificato non esiste, esso viene creato prima dell’apertura. Ecco la firma completa della funzione sqllite3_open():

int sqlite3_open(const char *filename, sqlite3 **database);

dove filename è il path al database file in UTF-8 e database è il riferimento alla struttura del database. Il risultato è un int, che corrisponde ad un codice relativo all’esito dell’operazione di apertura del database (SQLITE_OK se è andato tutto bene).

Ad esempio, per aprire il database file chiamato contacts.db e installato nella directory Documents dell’applicazione iPhone, ecco il comando (si assume che la variabile databasePath di tipo NSString contenga il path al database file):

sqlite3 *contactDB; //Declare a pointer to sqlite database structure

const char *dbpath = [databasePath UTF8String]; // Convert NSString to UTF-8

if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
   //Database opened successfully
} else {
   //Failed to open database
}

Preparazione ed Esecuzione di Statement SQL
Gli statement SQL vengono definiti su una struttura di tipo sqlite3_stmt usando la funzione sqlite3_prepare_v2(). Per esempio:

NSString *querySQL = @"SELECT address, phone FROM contacts”;

const char *query_stmt = [querySQL UTF8String];

if (sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
    //Statement prepared successfully
} else {
    //Statement preparation failed
}

Per lanciare gli statement SQL occorre chiamare le seguenti funzioni e passare il tipo sqlite3_stmt precedentemente definito:

sqlite3_step(statement);
sqlite3_finalize(statement);

sqlite3_step ritorna un codice con l’esito dell’operazione eseguita (ad esempio SQLITE_OK)

In alternativa, è possibile eseguire l’operazione di dichiarazione ed esecuzione degli statement direttamente con la funzione sqlite3_exec(), come illustrato in seguito.

Creazione di una tabella del database
Essendo SQLite un DBMS relazionale, i dati sono sempre strutturati in tabelle. Le tabelle si creano con lo statement SQL ‘CREATE TABLE’. Ecco il codice che permette di creare una tabella chiamata contacts, definendo lo statement ed eseguendolo direttamente con la funzione sqlite3_exec().

const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT, PHONE TEXT)";

if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) == SQLITE_OK)
{
     // SQL statement execution succeeded
}

Lettura dei dati da una tabella del database
Si recuperano i dati con lo statement SELECT. Occorre prima definire lo statement per leggere le righe dalla tabella di interesse e poi eseguirlo.
I dati estratti vengono memorizzati nella struttura sqlite3_stmt ed occorre, dunque, estrarre le colonne che interessano con la funzione sqlite3_column_<type>() , dove type  va sostituito con il tipo del dato estratto dalla corrispondente colonna della tabella. Occorre eseguire l’operazione di estrazione per ogni singola riga della tabella e, dunque, occorre utilizzare un ciclo while. Il secondo parametro della funzione sqlite_column_<type>() è il numero di colonna che deve essere estratta:

while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *addressField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)];

NSString *phoneField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 1)];

// Code to do something with extracted data here

[phoneField release];
[addressField release];
}
sqlite3_finalize(statement);

Chiudere un database SQLite
Quando l’applicazione ha finito di lavorare sul database è importante ricordarsi di chiuderlo. Basta eseguire il comando sqlite3_close() passandogli il riferimento al database:

sqlite3_close(contactDB);

3 pensieri su “[iOs] Introduzione a SQLite e integrazione in un XCode Project

  1. Ciao ,
    Avrei bisogno di un’informazione riguardo SQLite. Se io volessi caricare dati sul db in fase di installazione dell’app , come devo procedere?
    Grazie per l’attenzione

    • Ciao Carlo,
      ci sono fondamentalmente due strade:
      1. Inserisci nel tuo progetto il database nel bundle, dopo averlo già popolato con dei dati (con l’ausilio di un client sqllite tipo l’add-on di Mozilla). Al caricamento della tua app, salvi il file sqllite in una directory “backupabile”, come Documents
      2. Se vuoi caricare i dati a runtime, sempre in fase di loading della tua app, devi scrivere un metodo che prenda le query SQL, magari da un file testuale inserito nel bundle di progetto, e che li esegua riga per riga sul db sqllite

  2. Ciao,
    ho realizzato un APP che carica un database , dopo averlo già popolato con dei dati (con l’ausilio di un client sqllite tipo l’add-on di Mozilla), sul simulatore tutto ok, sul telefono mi apre sempre lo stesso db e non modifica niente .
    Perché???
    Grazie

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *


otto − = 3