2010-02-26 9 views
5

Quiero incluir una base de datos SQLite actualizada con una nueva versión de una aplicación. Mi aplicación copia el archivo de la base de datos en el directorio de Documentos al inicio. ¿Cuál es la mejor manera de hacer este tipo de control de versiones (además de usar Core Data)?SQLite, iPhone y control de versiones

Supongo que o bien una tabla especial de 'versión' en el archivo SQLite o un pequeño archivo de texto con el número de versión es el camino a seguir, pero me gustaría obtener las opiniones de otras personas.

Respuesta

0

Después de probar algunas técnicas, que terminó la adición de una mesa a mi base de datos de meta-información y la puesta en una columna de marca de tiempo. Cada vez que actualizo mi aplicación, compruebo la marca de tiempo de la base de datos del paquete con la marca de tiempo de la base de datos copiada (es decir, en el directorio de Documentos). Significa que debo recordar cambiar el valor de la marca de tiempo cuando actualizo, pero es simple y funciona.

El uso de marcas de tiempo de archivos no funcionó, ya que existe la posibilidad de que el usuario descargue la aplicación en la ventana de tiempo de Revisión de aplicaciones y termine en una base de datos copiada con una marca de tiempo más nueva que la del paquete.

0

La forma en que hago esto es mirando las listas de archivos. Si la fecha de modificación del archivo SQLite DB en el paquete .app es más reciente que la del directorio de documentos locales, entonces copio el del paquete .app sobre ... Aquí está el código que uso.

sqlite3 *dbh;   // Underlying database handle 
NSString *name;   // Database name (this is the basename part, without the extension) 
NSString *pathBundle; // Path to SQLite DB in the .app folder 
NSString *pathLocal; // Path to SQLite DB in the documents folder on the device 

- (BOOL)automaticallyCopyDatabase {        // Automatically copy DB from .app bundle to device document folder if needed 
    ES_CHECK(!dbh, NO, @"Can't autoCopy an already open DB") 
    ES_CHECK(name!=nil, NO, @"No DB name specified") 
    ES_CHECK(pathBundle!=nil, NO, @"No .app bundle path found, this is a cache DB") 
    ES_CHECK(pathLocal!=nil, NO, @"No local document path found, this is a read-only DB") 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSDictionary *localAttr = [fileManager fileAttributesAtPath:pathLocal traverseLink:YES]; 
    BOOL needsCopy = NO; 
    if (localAttr == nil) { 
     needsCopy = YES; 
    } else { 
     NSDate *localDate; 
     NSDate *appDBDate; 
     if (localDate = [localAttr objectForKey:NSFileModificationDate]) { 
      ES_CHECK([fileManager fileExistsAtPath:pathBundle], NO, @"Internal error: file '%@' does not exist in .app bundle", pathBundle) 
      NSDictionary *appDBAttr = [fileManager fileAttributesAtPath:pathBundle traverseLink:YES]; 
      ES_CHECK(appDBAttr!=nil, NO, @"Internal error: can't get attributes for '%@'", pathBundle) 
      appDBDate = [appDBAttr objectForKey:NSFileModificationDate]; 
      ES_CHECK(appDBDate!=nil, NO, @"Internal error: can't get last modification date for '%@'", pathBundle) 
      needsCopy = [appDBDate compare:localDate] == NSOrderedDescending; 
     } else { 
      needsCopy = YES; 
     } 
    } 
    if (needsCopy) { 
     NSError *error; 
     BOOL success; 
     if (localAttr != nil) { 
      success = [fileManager removeItemAtPath:pathLocal error:&error]; 
      ES_CHECK(success, NO, @"Can't delete file '%@'" ,pathLocal) 
     } 
     success = [fileManager copyItemAtPath:pathBundle toPath:pathLocal error:&error]; 
     ES_CHECK(success, NO, @"Can't copy database '%@' to '%@': %@", pathBundle, pathLocal, [error localizedDescription]) 
     ES_TRACE(@"Copied DB '%@' to '%@'", pathBundle, pathLocal) 
     return success; 
    } 
    return YES; 
} 

Las cosas ES_CHECK son sólo las macros que se expanden para nada en modo de lanzamiento, y lanzar una excepción en modo de depuración ... Son similares a esto:

#if ES_DEBUG 
#define ES_ASSERT(cond) assert(cond); 
#define ES_LOG(msg...) NSLog(msg); 
#define ES_TRACE(msg...) NSLog(msg); 
#else 
#define ES_ASSERT(cond) 
#define ES_LOG(msg...) 
#define ES_TRACE(msg...) 
#endif 
#define ES_CHECK(cond, ret, msg...) if (!(cond)) { ES_LOG(msg) ES_ASSERT(cond) return (ret); }  // Check with specified return value (when condition fails) 
+0

Esa macro 'ES_CHECK' es interesante ... En el modo de depuración, falla con una aserción, pero en el modo de liberación devuelve un código de error, por lo que el código real se comporta de forma muy diferente en los modos de depuración y liberación. Entonces, ¿solo puede probar que el código de llamada puede manejar el código de error devuelto al desactivar toda la depuración? –

+0

Sí. Pero ninguno de los controles falla, esa es la idea. Si alguna vez fallan en la producción, cortan la rutina al devolver un valor razonable en lugar de colgarse ... –

4

No hay necesidad de una mesa especializada. SQLite tiene un pragma para esto, llamado user_version. SQLite no utiliza este valor para nada, se deja completamente a la aplicación.

para leer la versión:

#pragma user_version; 

para establecer la versión:

#pragma user_version=1; 
+0

¿Cómo establecer programáticamente user_version en sqlite? Puedo leer user_version desde db pero por alguna razón la actualización falla ... !! alguna ayuda ? – Unicorn

+0

Eso es lo que sucede cuando trabajo desde la memoria. :) Respuesta arreglada. –

Cuestiones relacionadas