Skip to content

Instantly share code, notes, and snippets.

@dena-sohrabi
Created October 16, 2024 12:27
Show Gist options
  • Save dena-sohrabi/56f1c7cdf5cb6dde6f6ac38349de4be3 to your computer and use it in GitHub Desktop.
Save dena-sohrabi/56f1c7cdf5cb6dde6f6ac38349de4be3 to your computer and use it in GitHub Desktop.
Encrypted Database (GRDB + SQLCipher)
import Foundation
import GRDB
// MARK: - DB main class
public final class AppDatabase: Sendable {
public let dbWriter: any DatabaseWriter
public init(_ dbWriter: any GRDB.DatabaseWriter) throws {
self.dbWriter = dbWriter
try migrator.migrate(dbWriter)
}
}
// MARK: - Migrations
public extension AppDatabase {
var migrator: DatabaseMigrator {
var migrator = DatabaseMigrator()
#if DEBUG
// MAKE SURE: it does not enable in production
// Speed up development by nuking the database when migrations change
// Refrence <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations#The-eraseDatabaseOnSchemaChange-Option>
migrator.eraseDatabaseOnSchemaChange = true
#endif
migrator.registerMigration("v0") { _ in
// Create tables here
}
// Migrations for future application versions will be inserted here:
// migrator.registerMigration(...) { db in
// ...
// }
return migrator
}
}
// MARK: - Database Configuration
public extension AppDatabase {
/// - parameter base: A base configuration.
static func makeConfiguration(_ base: Configuration = Configuration()) -> Configuration {
var config = base
if let token = Auth.shared.getToken() {
config.prepareDatabase { db in
try db.usePassphrase(token)
}
} else {
config.prepareDatabase { db in
// Add a fake passphrase if the token does not exist; then, when it exists, use the token / change the password
try db.usePassphrase("123")
}
}
return config
}
static func authenticated() throws {
if let token = Auth.shared.getToken() {
try AppDatabase.shared.dbWriter.barrierWriteWithoutTransaction { db in
try db.changePassphrase(token)
// maybe dbPool.invalidateReadOnlyConnections()???
}
} else {
Log.shared.warning("AppDatabase.authenticated called without token")
}
}
}
// MARK: - Database Access: Reads
public extension AppDatabase {
/// Provides a read-only access to the database.
var reader: any GRDB.DatabaseReader {
dbWriter
}
}
// MARK: - The database for the application
public extension AppDatabase {
/// The database for the application
static let shared = makeShared()
private static func makeShared() -> AppDatabase {
do {
// Create the "Application Support/Database" directory if needed
let fileManager = FileManager.default
let appSupportURL = try fileManager.url(
for: .applicationSupportDirectory, in: .userDomainMask,
appropriateFor: nil, create: true
)
let directoryURL = appSupportURL.appendingPathComponent("Database", isDirectory: true)
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)
// Open or create the database
let databaseURL = directoryURL.appendingPathComponent("db.sqlite")
let config = AppDatabase.makeConfiguration()
let dbPool = try DatabasePool(path: databaseURL.path, configuration: config)
print("DB created in \(databaseURL) ")
// Create the AppDatabase
let appDatabase = try AppDatabase(dbPool)
return appDatabase
} catch {
fatalError("Unresolved error \(error)")
}
}
/// Creates an empty database for SwiftUI previews
static func empty() -> AppDatabase {
// Connect to an in-memory database
// Refrence https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections
let dbQueue = try! DatabaseQueue(configuration: AppDatabase.makeConfiguration())
return try! AppDatabase(dbQueue)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment