Prepara bases de datos SQLite para despliegue en Desktop, Web y iOS

En este tutorial veremos como puedes preparar tus bases de datos para desplegarlas y utilizarlas sin problemas en las diferentes plataformas soportadas.

Añadir la base de datos

Probablemente hayas creado tu base de datos SQLite utilizando un editor externo. De modo que el primer paso consiste en añadirla al proyecto.

Desde luego puedes hacerlo de muchas formas, pero te sugiero que lo hagas añadiendo un paso de compilación. De esta forma, el archivo se añadirá automáticamente en la ubicación que selecciones cada vez que compiles tu aplicación, e incluso podrás indicar ubicaciones diferentes (o bien bases de datos diferentes) tanto si vas a ejecutar la aplicación desde el IDE (depuración) o bien cuando vayas a realizar el despliegue final.

Para añadir un nuevo paso de compilación, lo primero es seleccionar la plataforma en la que estés interesado de entre los ajustes de compilación (en un proyecto Desktop) y utilices el menú contextual para seleccionar la opción “Add to Build Settings > Build Step > Copy Files”.

La anterior acción mostrará el Panel Inspector para el elemento recién añadido, donde podrás indicar el nombre del paso de compilación, indicar si dicha acción se realizará tanto para la depuración como el despliegue final de la aplicación e, importante, la ubicación en la que quieres copiar el archivo cuando la aplicación se compile.

En dicho menú verás una serie de rutas típicas prefijadas. Por ejemplo, una buena ubicación sería “Resources Folder”. Eso sí, no te olvides de añadir el archivo propiamente dicho utilizando para ello los botones situados en la barra superior del editor.

En los proyectos Web verás que el comportamiento será exactamente el mismo, incluso cuando utilizas Xojo Cloud como plataforma de despliegue.

En iOS, lo único que cambia es que deberás de seleccionar el icono con forma de iPhone para acceder al menú contextual; y que cada uno de los recursos que añadas deberán de ser firmados mediante el certificado correspondiente.

Copiar la base de datos al directorio de destino

Quizá pienses que con lo anterior ya está todo hecho, puesto que si el archivo de base de datos está copiado en un destino conocido… ya sabrías la ruta exacta a la hora de crear una nueva instancia de SQLiteDatabase o iOSSQLiteDatabase, ¿verdad?

Sin embargo, son muchos los motivos por los que NO deberías de hacer esto; especialmente porque la base de datos probablemente se abra en acceso de lectura y escritura… y si se escribe sobre la base de datos se estará modificando un recurso que invalidará la “firma” realizada mediante el certificado o proceso de empaquetado.

Por tanto, lo mejor que puedes hacer es detectar en un primer arranque de la app si la base de datos ya ha sido copiada previamente desde tu aplicación hacia un destino que no represente ningún problema para su posterior uso; como por ejemplo la carpeta de soporte de aplicaciones (en aplicaciones de escritorio), o Documents (en Xojo Cloud y iOS).

Por ejemplo, si nuestro archivo de base de datos se llama “EddiesElectronics.sqlite” y nuestra app se llama “XojoTest”, entonces podríamos añadir el siguiente fragmento de texto en el evento Open de un aplicación Desktop:

Var source As FolderItem = SpecialFolder.Resource("EddiesElectronics.sqlite")
Var name As String = app.ExecutableFile.name.NthField(".",1)
// We check if there is a folder with the App name in special Application Data
// if not, we create it and copy the database file from Resources bundle/directory
If Not (SpecialFolder.ApplicationData.Child(name).Exists And SpecialFolder.ApplicationData.Child(name).IsFolder) Then
SpecialFolder.ApplicationData.Child(name).CreateFolder
source.CopyTo(SpecialFolder.ApplicationData.Child(name))
End If

Try
// Create a SQLiteDatabase instance and try to open our database file from
// the path
pDatabase = New SQLiteDatabase
pDatabase.DatabaseFile = SpecialFolder.ApplicationData.Child(name).Child("EddiesElectronics.sqlite")
pDatabase.Connect

Catch e As DatabaseException
MessageBox e.Message
End Try

En el caso de que estemos creando una aplicación iOS, el código sería:

Var source As Xojo.IO.FolderItem = xojo.io.SpecialFolder.GetResource("EddiesElectronics.sqlite")

// We check if there is our database file already copied on the Documents Sandbox folder
// if not, we copy the database file from Resources bundle/directory
If Not xojo.io.SpecialFolder.Documents.Child("EddiesElectronics.sqlite").Exists Then
source.CopyTo(xojo.io.SpecialFolder.documents)
End If

Try
// Create a SQLiteDatabase instance and try to open our database file from
// the path
pDatabase = New iosSQLiteDatabase

Var f As FolderItem = xojo.io.SpecialFolder.Documents
pDatabase.DatabaseFile = f.Child("EddiesElectronics.sqlite")
Call pDatabase.Connect

Catch e As RuntimeException
MessageBox e.Reason
End Try

Si se trata de un despliegue en Xojo Cloud, el código es incluso más simple. En primer lugar utilizaremos el paso de compilación que vimos en el primer punto para que tenga los siguientes valores en el Panel Inspector:

  • Destination: Contents Folder
  • Subdirectory: Documents

Luego, nuestro código sería simplemente:

Try
pDatabase = new SQLiteDatabase
pDatabase.DatabaseFile = SpecialFolder.Documents.Child("EddiesElectronics.sqlite")
pDatabase.connect
Catch e as RuntimeException
MessageBox e.Reason
End Try

Por último está el caso de que se trate de una aplicación Web donde nosotros nos encargaremos del despliegue. Esto significa que tenemos total control sobre el destino (carpeta) en el que deseamos guardar todos los recursos que utilicemos. Por tanto, automatizar el proceso carecería de sentido en este caso y nos limitaríamos a apuntar en nuestro código hacia la ruta elegida.

Gestionar la copia y conexión para todos los casos

Lo anterior funciona, pero como ves has de escribir en todo momento el nombre del archivo correspondiente a la base de datos… y probablemente este varíe de una aplicación a otra.

También significa que tendrás que escribir los mismos fragmentos de código una y otra vez en cada nueva aplicación. ¿No sería genial ampliar las clases SQLiteDatabase y iOSSQLiteDatabase para simplificarlo?

Puedes hacerlo, y para ello empezaremos añadiendo un módulo (por ejemplo, con el nombre de “DatabaseExtensions”) donde añadiremos un par de métodos.

El primer método será el que utilizaremos tanto para las aplicaciones de escritorio, como de Consola y Web; puesto que todas ellas utilizan la clase SQLiteDatabase.

Por tanto, añade un nuevo método al módulo recién creado con la siguiente signatura:

OpenDatabase(extends db as SQLiteDatabase, databaseName as String)

Como puedes ver, se trata de ampliar la clase SQLiteDatabase añadiendo un nuevo método, tomando como parámetro el nombre del archivo que hemos de copiar al destino adecuado, asociarlo a la instancia de base de datos para, finalmente, realizar la conexión y comenzar a utilizarla.

El código de dicho método sería el siguiente:

#If TargetDesktop Or TargetConsole Or TargetWeb Then

Var source As FolderItem = SpecialFolder.Resource( databaseName)
Var name As String = app.ExecutableFile.name.NthField(".",1)
// We check if there is a folder with the App name in special Application Data
// if not, we create it and copy the database file from Resources bundle/directory
If Not (SpecialFolder.ApplicationData.Child(name).Exists And SpecialFolder.ApplicationData.Child(name).IsFolder) Then
SpecialFolder.ApplicationData.Child(name).CreateFolder
source.CopyTo(SpecialFolder.ApplicationData.Child(name))
End If

Try
db.DatabaseFile = SpecialFolder.ApplicationData.Child(name).Child(databaseName)
db.Connect

Catch e As DatabaseException
   MessageBox e.Message
End Try

#ElseIf TargetXojoCloud

Try

db.DatabaseFile = SpecialFolder.Documents.Child( databaseName)
db.connect

Catch e As RuntimeException
   MessageBox e.Reason
End Try

#EndIf

Ahora, teniendo en cuenta que nuestro proyecto cuenta con una propiedad pDatabase, su uso sería simplemente:

pDatabase = New SQLiteDatabase
pDatabase.OpenDatabase("EddiesElectronics.sqlite")

Con el método seleccionado, dirígete al apartado de Atributos del Panel Inspector (el icono de la rueda dentada) y desactiva la casilla correspondiente a iOS 64. De esta forma indicamos que no queremos que el método se compile en aplicaciones iOS.

El segundo método será el que utilizaremos para las aplicaciones iOS, con la siguiente signatura:

OpenDatabase(extends db as iOSSQLiteDatabase, databseName as String)

Encargado de ejecutar el siguiente código:

Var source As Xojo.IO.FolderItem = xojo.io.SpecialFolder.GetResource( databaseName )

// We check if there is our database file already copied on the Documents Sandbox folder
// if not, we copy the database file from Resources bundle/directory
If Not xojo.io.SpecialFolder.Documents.Child( databaseName ).Exists Then
  source.CopyTo(xojo.io.SpecialFolder.documents)
End If

Try
  // Create a SQLiteDatabase instance and try to open our database file from
  // the path
  
  Var f As FolderItem = xojo.io.SpecialFolder.Documents
  db.DatabaseFile = f.Child( databaseName )
  Call db.Connect
  
Catch e As RuntimeException
  MessageBox e.Reason
End Try

Ahora, con el método seleccionado, dirígete al apartado de Atributos del Panel Inspector (el icono de la rueda dentada). Aquí deberás de dejar marcada unicamente la casilla iOS 64 de la sección “Include In”.

De este modo, nos aseguraremos de que el método sólo se incluya para despliegues iOS.

Un comentario en “Prepara bases de datos SQLite para despliegue en Desktop, Web y iOS

  1. Phil Cumpston

    If Not SpecialFolder.ApplicationData.Child(nam).Child(Source).exists
    Should that not be “ApplicationData.Child(name)”?
    I note some differences in the English version compared to the Spanish version.
    Thank you for this article – it has made a significant difference to my code.

    ¿No debería ser ese “name”?

    Noto algunas diferencias en la versión en inglés en comparación con la versión en español.

    Gracias por este artículo, ha marcado una diferencia significativa en mi código.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *