This is the code for the article here
//
// SQLiteIntroApp.swift
// SQLiteIntro
//
import SwiftUI
import FMDB
struct User: Hashable, Decodable {
let username: String
let age: Int
init(username: String, age: Int) {
self.username = username
self.age = age
}
init?(from result: FMResultSet) {
if let username = result.string(forColumn: "username") {
self.username = username
self.age = Int(result.int(forColumn: "age"))
} else {
return nil
}
}
private enum CodingKeys : String, CodingKey {
case username = "first_name"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
username = try container.decode(String.self, forKey: .username)
age = Int.random(in: 1..<100)
}
}
final class DataWrapper: ObservableObject {
private let db: FMDatabase
@Published var users = [User]()
init(fileName: String = "test") {
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("\(fileName).sqlite")
let db = FMDatabase(url: fileURL)
guard db.open() else {
fatalError("Unable to open database")
}
do {
try db.executeUpdate("create table if not exists users(username varchar(255) primary key, age integer)", values: nil)
} catch {
fatalError("cannot execute query")
}
self.db = db
users = getAllUsers()
}
func insert(_ user: User) {
do {
try db.executeUpdate(
"""
insert into users (username, age)
values (?, ?)
""",
values: [user.username, user.age]
)
users.append(user)
} catch {
fatalError("cannot insert user: \(error)")
}
}
func getAllUsers() -> [User] {
var users = [User]()
do {
let result = try db.executeQuery("select username, age from users", values: nil)
while result.next() {
if let user = User(from: result) {
users.append(user)
}
}
return users
} catch {
return users
}
}
func getUser(with username: String) -> User? {
var user: User? = nil
do {
let result = try db.executeQuery("select username, age from users where username = ?", values: [username])
while result.next() {
user = User(from: result)
}
return user
} catch {
return user
}
}
}
@main
struct SQLiteIntroApp: App {
var db = DataWrapper()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(db)
}
}
}
struct ContentView: View {
@EnvironmentObject var db: DataWrapper
var body: some View {
NavigationView {
List(db.users, id: \.self) { user in
HStack {
Text(user.username)
Spacer()
Text("\(user.age)")
}
}
.navigationTitle("Users")
.toolbar {
ToolbarItem(id: "plus", placement: .navigationBarTrailing, showsByDefault: true) {
Button(action: {
createRandomUser()
}, label: {
Image(systemName: "plus")
})
}
}
}
}
private func createRandomUser() {
let url = URL(string: "https://random-data-api.com/api/name/random_name")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
fatalError("No data")
}
DispatchQueue.main.async {
let user = try! JSONDecoder().decode(User.self, from: data)
db.insert(user)
}
}
task.resume()
}
}