You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Hi all
~2 years experienced dev, new to Swift and GRDB
I am trying to setup a GRDB stack onto my app
My app is a dictionnary that will rely on a static database (basically all the words and translations) that will weigh ~200MB
I tried to follow the best practices as shown in the Demo Apps, and I ended up with an AppDatabase class that I call at the launch of my app and that handles the initialization of my database (run migrations + populates it). But it seems to me that it always run the migrations + the populateDb function at the launch of the app. This is not what I expect as it will take a lot of time for my app to open everytime...
I guess I could also preload an already existing database, but I am not sure...
Would I still be able to rely on the GRDB associationRelationships like hasMany, belongsTo like in my examples below ?
Could you give me the best practice to handle such matter ?
Here are my files that I simplified for this example, my database schema is way bigger and complex than that:
// Migration_01_CreateTables.swift
// GRDBPlayground
// Created by Léos on 28/07/2024.
import
GRDB
struct
Migration_01_CreateTables
{
static
func
performMigration
(
migrator
:
inout
DatabaseMigrator
)
{
migrator
.
registerMigration
(
"
CreateTables
"
)
{
db
in
// Create wordboxes table
try
db
.
create
(
table
:
"
wordboxes
"
)
{
t
in
t
.
autoIncrementedPrimaryKey
(
"
id
"
)
t
.
column
(
"
name
"
,
.
text
)
.
notNull
(
)
// Create translations table
try
db
.
create
(
table
:
"
translations
"
)
{
t
in
t
.
autoIncrementedPrimaryKey
(
"
id
"
)
t
.
column
(
"
wordboxID
"
,
.
integer
)
.
notNull
(
)
.
references
(
"
wordboxes
"
)
t
.
column
(
"
text
"
,
.
text
)
.
notNull
(
)
// models.swift
// GRDBPlayground
// Created by Léos on 27/07/2024.
import
Foundation
import
GRDB
enum
Language
:
String
{
case
english
=
"
en
"
case
french
=
"
fr
"
struct
Translation
:
Codable
{
var
id
:
Int64
?
var
wordboxID
:
Int64
?
var
text
:
String
extension
Translation
:
FetchableRecord
,
MutablePersistableRecord
{
static
var
databaseTableName
:
String
{
return
"
translations
"
enum
Columns
:
String
,
ColumnExpression
{
case
id
,
wordboxID
,
text
static
let
wordbox
=
belongsTo
(
WordBox
.
self
)
var
wordbox
:
QueryInterfaceRequest
<
WordBox
>
{
return
request
(
for
:
Translation
.
wordbox
)
mutating
func
didInsert
(
with rowID
:
Int64
,
for column
:
String
?
)
{
id
=
rowID
struct
WordBox
:
Codable
{
var
id
:
Int64
?
var
name
:
String
extension
WordBox
:
FetchableRecord
,
MutablePersistableRecord
{
static
var
databaseTableName
:
String
{
return
"
wordboxes
"
enum
Columns
:
String
,
ColumnExpression
{
case
id
,
name
static
let
translations
=
hasMany
(
Translation
.
self
)
var
translations
:
QueryInterfaceRequest
<
Translation
>
{
return
request
(
for
:
WordBox
.
translations
)
mutating
func
didInsert
(
with rowID
:
Int64
,
for column
:
String
?
)
{
id
=
rowID
// AppDatabase.swift
// GRDBPlayground
// Created by Léos on 27/07/2024.
import
Foundation
import
GRDB
import
os
.
log
struct
AppDatabase
{
private
let
dbWriter
:
any
DatabaseWriter
init
(
_ dbWriter
:
any
DatabaseWriter
)
throws
{
self
.
dbWriter
=
dbWriter
try
migrator
.
migrate
(
dbWriter
)
private
static
let
sqlLogger
=
OSLog
(
subsystem
:
Bundle
.
main
.
bundleIdentifier!
,
category
:
"
SQL
"
)
public
static
func
makeConfiguration
(
_ base
:
Configuration
=
Configuration
(
)
)
->
Configuration
{
var
config
=
base
if
ProcessInfo
.
processInfo
.
environment
[
"
SQL_TRACE
"
]
!=
nil
{
config
.
prepareDatabase
{
db
in
db
.
trace
{
os_log
(
"
%{public}@
"
,
log
:
sqlLogger
,
type
:
.
debug
,
String
(
describing
:
$0
)
)
#if DEBUG
config
.
publicStatementArguments
=
true
#endif
return
config
private
var
migrator
:
DatabaseMigrator
{
var
migrator
=
DatabaseMigrator
(
)
#if DEBUG
migrator
.
eraseDatabaseOnSchemaChange
=
true
#endif
// Register migrations
Migration_01_CreateTables
.
performMigration
(
migrator
:
&
migrator
)
return
migrator
extension
AppDatabase
{
static
let
shared
=
makeShared
(
)
private
static
func
makeShared
(
)
->
AppDatabase
{
do
{
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
)
let
databaseURL
=
directoryURL
.
appendingPathComponent
(
"
db.sqlite
"
)
NSLog
(
"
Database stored at
\(
databaseURL
.
path
)
"
)
let
dbPool
=
try
DatabasePool
(
path
:
databaseURL
.
path
,
configuration
:
AppDatabase
.
makeConfiguration
(
)
let
appDatabase
=
try
AppDatabase
(
dbPool
)
// Populate the database if it is empty
// try appDatabase.populate()
return
appDatabase
}
catch
{
fatalError
(
"
Unresolved error
\(
error
)
"
)
extension
AppDatabase
{
var
reader
:
DatabaseReader
{
dbWriter
// GRDBPlaygroundApp.swift
// GRDBPlayground
// Created by Léos on 27/07/2024.
import
SwiftUI
@
main
struct
GRDBPlaygroundApp
:
App
{
init
(
)
{
// Initialize the shared database instance
_
=
AppDatabase
.
shared
var
body
:
some
Scene
{
WindowGroup
{
ContentView
(
)
.
environment
(
\
.
appDatabase
,
AppDatabase
.
shared
)
struct
AppDatabaseKey
:
EnvironmentKey
{
static
let
defaultValue
:
AppDatabase
=
AppDatabase
.
shared
extension
EnvironmentValues
{
var
appDatabase
:
AppDatabase
{
get
{
self
[
AppDatabaseKey
.
self
]
}
set
{
self
[
AppDatabaseKey
.
self
]
=
newValue
}