Add SQLite to save tasks

This commit is contained in:
2023-07-19 13:01:33 +02:00
parent 0ff3803059
commit 3cdad5a4d7
10 changed files with 319 additions and 20 deletions

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "arrow-down.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 4.7725C0 4.5825 0.08 4.3825 0.23 4.2325C0.53 3.9225 1.02 3.9225 1.32 4.2325L8 10.9125L14.68 4.2325C14.98 3.9325 15.47 3.9325 15.77 4.2325C16.07 4.5325 16.07 5.0225 15.77 5.3225L8.54 12.5525C8.24 12.8525 7.75 12.8525 7.45 12.5525L0.23 5.3225C0.08 5.1725 0 4.9725 0 4.7725Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@ -14,7 +14,7 @@ struct LutoApp: App {
var body: some Scene {
WindowGroup(id: "MainWindow") {
MainView()
MainView(viewModel: TaskViewModel())
}
}
}
@ -40,8 +40,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
self.popover = NSPopover()
self.popover.contentSize = NSSize(width: 500, height: 500)
self.popover.behavior = .transient
self.popover.contentViewController = NSHostingController(rootView: MainView()
)
self.popover.contentViewController = NSHostingController(rootView: MainView(viewModel: TaskViewModel()))
}
@objc func togglePopover() {

View File

@ -7,7 +7,20 @@
import Foundation
struct Task {
class Task {
let id: Int64
var title: String
var description: String
var body: String
init(title: String, body: String) {
self.id = 0
self.title = title
self.body = body
}
init(id: Int64, title: String, body: String) {
self.id = id
self.title = title
self.body = body
}
}

View File

@ -0,0 +1,138 @@
//
// TaskDataStore.swift
// Luto
//
// Created by Pierre Boulc'h on 17/07/2023.
//
import Foundation
import SQLite
class TaskDataStore {
static let DIR_TASK_DB = "TaskDB"
static let STORE_NAME = "task.sqlite3"
private let tasks = Table("tasks")
private let id = Expression<Int64>("id")
private let title = Expression<String>("title")
private let body = Expression<String>("body")
static let shared = TaskDataStore()
private var db: Connection? = nil
private init() {
if let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let dirPath = docDir.appendingPathComponent(Self.DIR_TASK_DB)
do {
try FileManager.default.createDirectory(atPath: dirPath.path, withIntermediateDirectories: true, attributes: nil)
let dbPath = dirPath.appendingPathComponent(Self.STORE_NAME).path
db = try Connection(dbPath)
createTable()
print("SQLiteDataStore init successfully at: \(dbPath) ")
} catch {
db = nil
print("SQLiteDataStore init error: \(error)")
}
} else {
db = nil
}
}
private func createTable() {
guard let database = db else {
return
}
do {
try database.run(tasks.create { table in
table.column(id, primaryKey: .autoincrement)
table.column(title)
table.column(body)
})
print("Table Created...")
} catch {
print(error)
}
}
func insert(name: String, description: String) -> Int64? {
guard let database = db else { return nil }
let insert = tasks.insert(self.title <- name,
self.body <- description)
do {
let rowID = try database.run(insert)
return rowID
} catch {
print(error)
return nil
}
}
func getAllTasks() -> [Task] {
var tasks: [Task] = []
guard let database = db else { return [] }
do {
for task in try database.prepare(self.tasks) {
tasks.append(Task(id: task[id], title: task[title], body: task[body]))
}
} catch {
print(error)
}
return tasks
}
func findTask(taskId: Int64) -> Task? {
var task: Task = Task(id: taskId, title: "", body: "")
guard let database = db else { return nil }
let filter = self.tasks.filter(id == taskId)
do {
for t in try database.prepare(filter) {
task.title = t[title]
task.body = t[body]
}
} catch {
print(error)
}
return task
}
func update(id: Int64, name: String, date: Date = Date(), status: Bool = false) -> Bool {
guard let database = db else { return false }
let task = tasks.filter(self.id == id)
do {
let update = task.update([
title <- title,
self.body <- body,
])
if try database.run(update) > 0 {
return true
}
} catch {
print(error)
}
return false
}
func delete(id: Int64) -> Bool {
guard let database = db else {
return false
}
do {
let filter = tasks.filter(self.id == id)
try database.run(filter.delete())
return true
} catch {
print(error)
return false
}
}
}

View File

@ -15,9 +15,10 @@ struct MainView: View {
case descriptionField
}
@ObservedObject var viewModel: TaskViewModel
@State var taskTitle = ""
@State var taskDescription = ""
@State var listTask: [Task] = []
@State var showAdditionnalFields = false
@ -28,12 +29,12 @@ struct MainView: View {
Text("Mes tâches")
ScrollView {
LazyVStack(alignment: .leading) {
ForEach(Array(listTask.enumerated()), id: \.offset) { index, task in
ForEach(Array(viewModel.allTask.enumerated()), id: \.offset) { index, task in
HStack {
VStack(alignment: .leading) {
Text("\(task.title)").font(.system(size: 16))
if !task.description.isEmpty {
Text("\(task.description)")
if !task.body.isEmpty {
Text("\(task.body)")
}
}
.padding(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 8))
@ -60,6 +61,8 @@ struct MainView: View {
}
}.onSubmit {
addTask()
}.onAppear {
focusState = .titleField
}
Button(action: {
addTask()
@ -74,6 +77,13 @@ struct MainView: View {
TextField("Description ...", text: $taskDescription).focused($focusState, equals: .descriptionField).textFieldStyle(OvalTextFieldStyle()).padding(EdgeInsets(top: 8, leading: 0, bottom: 0, trailing: 0)).onSubmit {
addTask()
}
} else {
Button(action: {
showAdditionnalFields = true
}, label: {
Image("arrow-down").tint(Color.white)
}).buttonStyle(PlainButtonStyle())
}
}
.padding()
@ -83,7 +93,7 @@ struct MainView: View {
func addTask() {
if !taskTitle.isEmpty {
withAnimation {
listTask.append(Task(title: taskTitle, description: taskDescription))
viewModel.addTask(name: taskTitle, body: taskDescription)
}
taskTitle = ""
taskDescription = ""
@ -93,14 +103,8 @@ struct MainView: View {
}
func removeTask(index: Int) {
_ = withAnimation {
listTask.remove(at: index)
withAnimation {
viewModel.deleteTask(at: IndexSet([index]))
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}

View File

@ -0,0 +1,39 @@
//
// TaskViewModel.swift
// Luto
//
// Created by Pierre Boulc'h on 18/07/2023.
//
import Foundation
class TaskViewModel: ObservableObject {
@Published var allTask: [Task] = []
init() {
getTaskList()
}
func addTask(name: String, body: String) {
let id = TaskDataStore.shared.insert(name: name, description: body)
if id != 0 {
getTaskList()
}
}
func getTaskList() {
allTask = TaskDataStore.shared.getAllTasks()
}
func deleteTask(at indexSet: IndexSet) {
let id = indexSet.map { self.allTask[$0].id }.first
if let id = id {
let delete = TaskDataStore.shared.delete(id: id)
if delete {
getTaskList()
}
}
}
}