I am trying to follow this guide here: https://elliotekj.com/2019/12/11/sqlite-ios-getting-started-with-grdb and while helpful, it's not exactly a tutorial.
So far I have this code:
AppDatabase
import GRDB
var dbQueue: DatabaseQueue!
class AppDatabase {
static func setup(for application: UIApplication) throws {
let databaseURL = try FileManager.default
.url(for: .applicationDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("db.sqlite")
dbQueue = try DatabaseQueue(path: databaseURL.path)
}
}
And in my AppDelegate this code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
try! AppDatabase.setup(for: application)
return true
}
It think the above is correct. Currently, I'm manipulating my db via Navicat, so I know my table is fine. But now what do I need to do to be able to simply read my table?
Here is my SwiftUI ContentView:
import SwiftUI
import GRDB
struct ContentView: View {
@State private var firstName: String = "Saul"
@State private var dateOfBirth: String = "1992-05-12"
var body: some View {
ZStack {
VStack{
HStack {
Text("Name")
Spacer()
TextField(" Enter text ", text: $firstName)
.frame(width: 160, height: 44)
.padding(4)
.border(Color.blue)
}.frame(width:300)
HStack {
Text("Date of Birth")
Spacer()
TextField(" Enter text ", text: $dateOfBirth)
.frame(width: 160, height: 44)
.padding(4)
.border(Color.blue)
}.frame(width:300)
}.foregroundColor(.gray)
.font(.headline)
VStack {
Spacer()
Button(action: {
}) {
Text("Add").font(.headline)
}
.frame(width: 270, height: 64)
.background(Color.secondary).foregroundColor(.white)
.cornerRadius(12)
}
}
}
}
private func readPerson() {
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Person {
var personID: Int64?
var firstName: String
var lastName: String?
var dateOfBirth: String
}
extension Person: Codable, FetchableRecord, MutablePersistableRecord {
// Define database columns from CodingKeys
private enum Columns {
static let personID = Column(CodingKeys.personID)
static let firstName = Column(CodingKeys.firstName)
static let lastName = Column(CodingKeys.lastName)
static let dateOfBirth = Column(CodingKeys.dateOfBirth)
}
// Update a person id after it has been inserted in the database.
mutating func didInsert(with rowID: Int64, for column: String?) {
personID = rowID
}
}
I really don't understand what to write in readPerson() or where to place it in my view. For now, I'd be happy to populate my textFields from the table, but ext of course, I'd like to add persons using the button.
Ok, so now that I've had time to delve into this, here is the solution I found.
Assuming a database is already attached, I created an
envSetting.swift
file to hold anObservableObject
. Here is that file, which I feel is fairly self-explanatory (It's a basicObservableObject
set-up see https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-observedobject-to-manage-state-from-external-objects):In this code, the
getAthletes
function returns an array ofAthlete
objects. It resides in anAthlete.swift
file, the bulk of which comes from the GRDB demo app with specific edits and functions for my case:And this is my
ContentView.swift
file:And don't forget to add the environemnt to the
SceneDelegate
:For me this works, so far, after limited testing. I'm not sure it is the best way to go.
Essentially, we are setting up the
ObservableObject
to query the DB file anytime we make a pertinent change to it. That's why you see me calling theenv.updateAthletes()
funtion in.onDelete
and in the "Done" button action for 'AddAthlete()'.I'm not sure otherwise how to let SwiftUI know the DB has changed. GRDB does have some kind of observation code going on, but it's really, really opaque to me how to use that, or even if that is the correct solution here.
I hope this is helpful to people.