Crear una aplicación con SQLite en iOS 6 (Parte I)

En el siguiente tutorial vamos a explicar cómo crear una aplicación sencilla que nos permita realizar las cuatro operaciones básicas contra una base de datos SQL Lite, esto es: altas, bajas, modificaciones y consultas contra esta base de datos. En esta primera parte del tutorial vamos a crear la base de datos mediante un gestor, vamos a introducir unos pocos datos de prueba y después crearemos una aplicación que nos mostrará estos datos en la pantalla de nuestro dispositivo mediante una tabla.

En primer lugar, necesitamos crear las tablas de nuestra base de datos. Para ello, nos descargaremos el addon de Firefox “SQLite Database Manager”. Si no utilizamos Firefox, también podemos descargar un gestor desde SQLAbs. Ejecutamos el addon y creamos una nueva base de datos, pinchando sobre el botón que se indica en la imagen:

Foto1 Crear una aplicación con SQLite en iOS 6 (Parte I)

Elegimos el nombre que queramos. Una vez aceptemos, nos creará un fichero .sqlite con el nombre que hayamos elegido en la ubicación que queramos. Para este tutorial, hemos elegido el nombre vehiculos.sqlite. A continuación hacemos click en el botón de Crear Tabla y podremos crear la primera tabla de nuestra nueva base de datos. Rellenamos los campos tal y como se ve en la siguiente imagen:

Foto21 Crear una aplicación con SQLite en iOS 6 (Parte I)

Una vez creada nuestra tabla, vamos a insertar algunos datos de prueba en ella. Para ello, desplegamos la sección de Tables y después seleccionamos nuestra tabla. Con nuestra tabla seleccionada, en la parte derecha de la ventana veremos un botón que nos permitirá añadir una nueva fila a la tabla. En la siguiente imagen vemos de qué botón se trata:

Foto3 Crear una aplicación con SQLite en iOS 6 (Parte I)

Nos aparecerá una ventana con los tres campos de la tabla para rellenar. Insertamos los datos que queramos (para este tutorial, con tres vehículos nos bastará) rellenando todos los campos y confirmando los cambios cuando el programa nos lo pida. Repetimos la operación hasta completar el número de vehículos que queramos. Para finalizar, una vez hayamos insertado el último, hacemos click en el botón de cancelar y nos aparecerá lo siguiente por pantalla:

Foto4 Crear una aplicación con SQLite en iOS 6 (Parte I)

Con esto terminan nuestras operaciones con el gestor de base de datos. A continuación vamos a crear el proyecto de XCode con el que crearemos nuestra aplicación. Elegimos un proyecto de tipo Single View Application y lo nombramos como queramos, en este caso hemos elegido el nombre VehiculosCRUD. Indicaremos que la aplicación será para iPhone, utilizará Storyboards y también utilizará ARC.

Una vez hayamos creado el proyecto, nos dirigimos a la sección de Linked Frameworks and Libraries y añadimos una nueva entrada con el botón +. En el buscador que se nos abre, buscamos la librería libsqlite3.0.dylib y la añadimos. Ahora, buscamos el fichero que contiene nuestra base de datos, la cual hemos creado antes y lo arrastramos a la lista de ficheros del proyecto. En la pantalla que aparece, dejamos las opciones tal y como se ven en la siguiente imagen:

Foto5 Crear una aplicación con SQLite en iOS 6 (Parte I)

Ahora vamos a crear las operaciones. Primero crearemos un grupo y lo haremos desde el menú File y seleccionando la opción New Group. Le pondremos como nombre modelo. Una vez creado el nuevo grupo, crearemos la clase que nos mapeará la tabla de vehiculo. La clase la crearemos seleccionando el nuevo grupo y, desde el menú contextual, seleccionaremos New File. Elegimos Objective-C class, le indicamos que será una subclase de NSObject y la llamaremos Vehiculo. Una vez la creemos, le añadimos las propiedades necesarias para mapear los campos de la tabla de base de datos, tal y como podemos ver en el siguiente código:

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

@interface Vehiculo : NSObject{
	NSInteger vehiculoID;
	NSString *nombreVehiculo;
	NSInteger numeroRuedas;
}

@property (nonatomic, assign) NSInteger vehiculoID;
@property (nonatomic, retain) NSString *nombreVehiculo;
@property (nonatomic, assign) NSInteger numeroRuedas;

@end

Vehiculo.m
#import "Vehiculo.h"

@implementation Vehiculo

@synthesize vehiculoID;
@synthesize nombreVehiculo;
@synthesize numeroRuedas;

@end

A continuación creamos la clase que nos permitirá realizar las operaciones contra la base de datos. Esta clase también la crearemos dentro del grupo modelo. Repetimos la misma operación con la que creamos la clase Vehiculo: Objective-C class, subclase de NSObject y la llamaremos VehiculoDAO. Una vez creada, le añadiremos las propiedades necesarias y definiremos los métodos de obtener datos y obtener la ruta de base de datos, tal y como podemos ver en el siguiente código:

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

@interface VehiculoDAO : NSObject{
	sqlite3 *bd;
}

- (NSMutableArray *) obtenerVehiculos;
- (NSString *) obtenerRutaBD;

@end
VehiculoDAO.m
#import "VehiculoDAO.h"
#import "Vehiculo.h"

@implementation VehiculoDAO

- (NSString *) obtenerRutaBD{
    NSString *dirDocs;
    NSArray *rutas;
    NSString *rutaBD;

    rutas = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    dirDocs = [rutas objectAtIndex:0];

    NSFileManager *fileMgr = [NSFileManager defaultManager];
    rutaBD = [[NSString alloc] initWithString:[dirDocs stringByAppendingPathComponent:@"vehiculos.sqlite"]];

    if([fileMgr fileExistsAtPath:rutaBD] == NO){
        [fileMgr copyItemAtPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"vehiculos.sqlite"] toPath:rutaBD error:NULL];
    }

    return rutaBD;
}
- (NSMutableArray *) obtenerVehiculos{
	NSMutableArray *listaVehiculos = [[NSMutableArray alloc] init];
	NSString *ubicacionDB = [self obtenerRutaBD];

	if(!(sqlite3_open([ubicacionDB UTF8String], &bd) == SQLITE_OK)){
		NSLog(@"No se puede conectar con la BD");
	}

	const char *sentenciaSQL = "SELECT id, nombre_vehiculo, numero_ruedas FROM vehiculo";
	sqlite3_stmt *sqlStatement;

	if(sqlite3_prepare_v2(bd, sentenciaSQL, -1, &sqlStatement, NULL) != SQLITE_OK){
		NSLog(@"Problema al preparar el statement");
	}

	while(sqlite3_step(sqlStatement) == SQLITE_ROW){
		Vehiculo *vehiculo = [[Vehiculo alloc] init];
		vehiculo.vehiculoID = sqlite3_column_int(sqlStatement, 0);
		vehiculo.nombreVehiculo = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];
		vehiculo.numeroRuedas = sqlite3_column_int(sqlStatement, 2);

		[listaVehiculos addObject:vehiculo];
	}

	return listaVehiculos;
}

Hemos creado el método obtenerRutaBD con vistas a la segunda parte de este tutorial, en el que realizaremos inserciones, modificaciones y borrados. El propósito de este método es el de copiar al directorio de Documents del dispositivo el fichero .sqlite siempre y cuando éste no exista. Esto es debido a que no se puede escribir en ficheros que se encuentren en la raíz de la aplicación (main bundle) ya que es de sólo lectura. Por otro lado, en el método obtenerVehiculos podemos ver cómo se obtiene la ruta de nuestro fichero de base de datos, se abre la conexión, se prepara la sentencia y se lanza. Todo esto puede hacerse gracias a los métodos sqlite3_open, sqlite3_prepare_v2 y sqlite3_step.

Ahora crearemos nuestro storyboard para poder gestionar todas estas operaciones. Primero de todo, añadiremos a nuestro storyboard un TableViewController. Una vez añadido, seleccionamos el controller y una vez hecho esto, abrimos el menú Editor y seleccionamos la opción Embed In -> Navigation Controller. También añadiremos un ViewController al storyboard, y a este nuevo ViewController le añadimos dos UILabel y dos UITextField y le ponemos como identificador el nombre visualización, quedando finalmente nuestro storyboard tal y como muestra la siguiente imagen:

Foto6 Crear una aplicación con SQLite en iOS 6 (Parte I)

También seleccionamos la celda del TableViewController y le ponemos como identificador el nombre celda.

Ahora vamos a centrarnos en mostrar datos en el TableViewController. Lo primero que haremos será crear una nueva clase que sea una subclase de UITableViewController, la llamaremos ListaVehiculosViewController y en nuestro storyboard se la asignaremos al TableViewController. En esta nueva clase definiremos un objeto de tipo VehiculoDAO y otro de tipo NSMutableArray. En el método viewDidLoad inicializaremos estos dos objetos y, una vez inicializados, lanzaremos una consulta para que nos devuelva todos los vehículos de nuestra tabla con este código:

ListaVehiculosViewController.h

#import <UIKit/UIKit.h>
#import "VehiculoDAO.h"

@interface ListaVehiculosViewController : UITableViewController{
	VehiculoDAO *dao;
	NSMutableArray *vehiculos;
}

@property (nonatomic, strong) VehiculoDAO *dao;
@property (nonatomic, strong) NSMutableArray *vehiculos;

@end

ListaVehiculosViewController.m

@synthesize dao;
@synthesize vehiculos;

- (void)viewDidLoad
{
    dao = [[VehiculoDAO alloc] init];
    vehiculos = [[NSMutableArray alloc] init];
    vehiculos = [dao obtenerVehiculos];

    [super viewDidLoad];
}

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [vehiculos count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"celda";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    cell.textLabel.text = [[vehiculos objectAtIndex:[indexPath row]] valueForKey:@"nombreVehiculo"];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{	
    ModVehiculoViewController *destino = [self.storyboard instantiateViewControllerWithIdentifier:@"visualizacion"];
    Vehiculo *tmp = [vehiculos objectAtIndex:[indexPath row]];
    destino.vehiculo = tmp;

    [self.navigationController pushViewController:destino animated:YES];
}

Como podemos ver en el código, le indicaremos que sólo tendrá una sección y el número de filas será el tamaño del array que nos devuelva la consulta. También hemos implementado el método didSelectRowAtIndexPath para hacer la transición desde la celda hasta el ViewController que nos enseñará los datos del vehículo.

Por último, nos centraremos en el ViewController de destino. Para ello, vamos a aprovecharnos de la clase ViewController que se crea por defecto cuando creamos el proyecto. Primero de todo, la renombramos a ModVehiculoViewController y se la asignamos en el storyboard al ViewController. Una vez hecho esto, en la clase definiremos los IBOutlet necesarios, un objeto de tipo VehiculoDAO y otro objeto de tipo Vehiculo. Por último, definiremos también un método para ocultar el teclado. La clase quedará como puede verse en el siguiente código:

ModVehiculoViewController.h
#import <UIKit/UIKit.h>
#import "VehiculoDAO.h"
#import "Vehiculo.h"

@interface ModVehiculoViewController : UIViewController{
    IBOutlet UILabel *etqNombre;
    IBOutlet UILabel *etqRuedas;
    IBOutlet UITextField *txtNombre;
    IBOutlet UITextField *txtRuedas;
    VehiculoDAO *dao;
    Vehiculo *vehiculo;
}

@property (nonatomic, strong) IBOutlet UILabel *etqNombre;
@property (nonatomic, strong) IBOutlet UILabel *etqRuedas;
@property (nonatomic, strong) IBOutlet UITextField *txtNombre;
@property (nonatomic, strong) IBOutlet UITextField *txtRuedas;
@property (nonatomic, strong) Vehiculo *vehiculo;
@property (nonatomic, strong) VehiculoDAO *dao;

- (IBAction) ocultarTeclado:(id)sender;

@end

ModVehiculoViewController.m
@synthesize etqNombre;
@synthesize etqRuedas;
@synthesize txtNombre;
@synthesize txtRuedas;
@synthesize dao;
@synthesize vehiculo;

-(void) touchesBegan :(NSSet *) touches withEvent:(UIEvent *)event{

    [txtNombre resignFirstResponder];	
    [txtRuedas resignFirstResponder];
    [super touchesBegan:touches withEvent:event ];

}

- (IBAction)ocultarTeclado:(id)sender{

}

- (void)viewDidLoad
{
    [txtNombre setText:vehiculo.nombreVehiculo];
    [txtRuedas setText:[NSString stringWithFormat:@"%d", vehiculo.numeroRuedas]];
    [txtRuedas setKeyboardType:UIKeyboardTypeNumberPad];

    dao = [[VehiculoDAO alloc] init];

    [super viewDidLoad];
}

Y con esto ya tenemos la primera parte de nuestro tutorial terminada. Bastará con ejecutar el simulador para ver el resultado:

Foto7 31 Crear una aplicación con SQLite en iOS 6 (Parte I)

¿Conoces nuestro curso Online de iOS 7?