I am trying to encrypt my SQLDelight database using SqlCipher. I've found one appropriate example on GitHub, however, it uses outdated cwac-saferoom which connects Room with SQLCipher for Android. The oudated library mentions to use SQLCipher for Android. So, taking into consideration the NoteDelight repository from the GitHub mentioned above and the SQLCipher Android documentation I tried implementing this approach. However, I cannot seem to understand how to make this work.
- I have followed SQLDelight documentation and implemented my .sq files under src/main/sqldelight as well added necessary imports.
- Under the commonMain package I have
expect val databaseModule: Module
val repositoriesModule = module {
single { AddressRepository(get()) }
// more repositories
}
class AddressRepository(private val database: Database) {
fun insertAddress(address: Address) =
database.addressQueries.insertAddress(
// parameters
)
// more functions
}
- Now in androidMain I provide the actual implementation of databaseModule:
actual val databaseModule: Module = module {
single<Database> { AndroidDatabase(get()) }
}
abstract class DatabaseQueries {
abstract val addressQueries: AddressQueries
// more queries
}
abstract class Database : DatabaseQueries() {
abstract fun initializeDatabase(passphrase: CharSequence = ""): DatabaseHolder
// encrypt, decrypt, repass and other abstract methods
}
class AndroidDatabase(private val context: Context) : Database() {
private var databaseHolder: DatabaseHolder? = null
override val addressQueries: AddressQueries
get() = initializeDatabase("").addressQueries
override fun initializeDatabase(passphrase: CharSequence): DatabaseHolder {
var instance = databaseHolder
if (instance == null) {
instance = AndroidDatabaseHolder(context, SpannableStringBuilder(passphrase))
databaseHolder = instance
}
return instance
}
// other methods that override abstract methods
}
- I implement my database holder class
abstract class DatabaseHolder : DatabaseQueries() {
abstract val database: MyDatabase
abstract val driver: SqlDriver
}
class AndroidDatabaseHolder(
context: Context,
passphrase: CharSequence
) : DatabaseHolder() {
init {
System.loadLibrary("sqlcipher")
}
private val encryptedDatabase = SQLiteDatabase.openOrCreateDatabase(
context.getDatabasePath(DATABASE_NAME),
passphrase.toString(),
null
)
override val driver = AndroidSqliteDriver(encryptedDatabase)
override val database: MyDatabase = createQueryWrapper(driver)
override val addressQueries: AddressQueries = database.addressQueries
// more overriden queries
override fun close() {
driver.close()
}
}
- Util method
fun createQueryWrapper(driver: SqlDriver): MyDatabase {
return MyDatabase(
driver = driver,
photoAdapter = /* My adapter */)
)
}
I first open clean app and create the database using initializeDatabase
with my provided password. Then in the login page I provide the password and the app calls the initializeDatabase
method again. Then I get to the main page where all the repositories get all the records from the database. That's where the error occurs for each of the repository:
android.database.sqlite.SQLiteException: no such table: address: , while compiling: SELECT * FROM address
Is the approach I am following is correct to implement SqlCipher encryption together with SQLDelight for the best practices? Are there any examples I have missed for correct implementation? And how to fix my error? Thanks.