From 280a0b917ed6a13ba434208b0cf1bc0af5379925 Mon Sep 17 00:00:00 2001 From: Connor Holland Date: Sat, 13 Jun 2020 13:45:38 -0600 Subject: [PATCH 1/2] Task --- .../ModelControllers/TaskController.swift | 9 ++ .../ViewControllers/ButtonTableViewCell.swift | 24 +++++ .../TaskDetailTableViewController.swift | 90 +++++++++++++++++++ .../TaskListTableViewController.swift | 90 +++++++++++++++++++ Task/Task/Model/CoreDataStack.swift | 9 ++ Task/Task/Model/DateHelpers.swift | 9 ++ Task/Task/Model/Task+Convenience.swift | 9 ++ 7 files changed, 240 insertions(+) create mode 100644 Task/Task/Controllers/ModelControllers/TaskController.swift create mode 100644 Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift create mode 100644 Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift create mode 100644 Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift create mode 100644 Task/Task/Model/CoreDataStack.swift create mode 100644 Task/Task/Model/DateHelpers.swift create mode 100644 Task/Task/Model/Task+Convenience.swift diff --git a/Task/Task/Controllers/ModelControllers/TaskController.swift b/Task/Task/Controllers/ModelControllers/TaskController.swift new file mode 100644 index 0000000..940fb93 --- /dev/null +++ b/Task/Task/Controllers/ModelControllers/TaskController.swift @@ -0,0 +1,9 @@ +// +// TaskController.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import Foundation diff --git a/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift b/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift new file mode 100644 index 0000000..8acfc6c --- /dev/null +++ b/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift @@ -0,0 +1,24 @@ +// +// ButtonTableViewCell.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import UIKit + +class ButtonTableViewCell: UITableViewCell { + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + +} diff --git a/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift b/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift new file mode 100644 index 0000000..3e00179 --- /dev/null +++ b/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift @@ -0,0 +1,90 @@ +// +// TaskDetailTableViewController.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import UIKit + +class TaskDetailTableViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift b/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift new file mode 100644 index 0000000..f6d7c21 --- /dev/null +++ b/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift @@ -0,0 +1,90 @@ +// +// TaskListTableViewController.swift +// Task +// +// Created by Connor Holland on 6/13/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import UIKit + +class TaskListTableViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/Task/Task/Model/CoreDataStack.swift b/Task/Task/Model/CoreDataStack.swift new file mode 100644 index 0000000..32397e5 --- /dev/null +++ b/Task/Task/Model/CoreDataStack.swift @@ -0,0 +1,9 @@ +// +// CoreDataStack.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import Foundation diff --git a/Task/Task/Model/DateHelpers.swift b/Task/Task/Model/DateHelpers.swift new file mode 100644 index 0000000..e5cd421 --- /dev/null +++ b/Task/Task/Model/DateHelpers.swift @@ -0,0 +1,9 @@ +// +// DateHelpers.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import Foundation diff --git a/Task/Task/Model/Task+Convenience.swift b/Task/Task/Model/Task+Convenience.swift new file mode 100644 index 0000000..35d1700 --- /dev/null +++ b/Task/Task/Model/Task+Convenience.swift @@ -0,0 +1,9 @@ +// +// Task+Convenience.swift +// Task +// +// Created by Connor Holland on 6/11/20. +// Copyright © 2020 Karl Pfister. All rights reserved. +// + +import Foundation From ae63916c43e2ee66823545fd8b2e59ff4384eb20 Mon Sep 17 00:00:00 2001 From: Connor Holland Date: Sat, 13 Jun 2020 21:07:48 -0600 Subject: [PATCH 2/2] complete --- Task/Task.xcodeproj/project.pbxproj | 52 ++++ .../ModelControllers/TaskController.swift | 64 +++++ .../ViewControllers/ButtonTableViewCell.swift | 34 ++- .../TaskDetailTableViewController.swift | 114 ++++----- .../TaskListTableViewController.swift | 129 ++++++---- Task/Task/Model/CoreDataStack.swift | 16 ++ Task/Task/Model/DateHelpers.swift | 8 + Task/Task/Model/Task+Convenience.swift | 11 + .../Task.xcdatamodel/contents | 11 +- .../Storyboards/Base.lproj/Main.storyboard | 235 +++++++++++++++++- 10 files changed, 551 insertions(+), 123 deletions(-) diff --git a/Task/Task.xcodeproj/project.pbxproj b/Task/Task.xcodeproj/project.pbxproj index 66f73a4..9c61f27 100644 --- a/Task/Task.xcodeproj/project.pbxproj +++ b/Task/Task.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + DE738E6B2492D91900C27A4F /* TaskDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E6A2492D91900C27A4F /* TaskDetailTableViewController.swift */; }; + DE738E6D2492D95E00C27A4F /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E6C2492D95E00C27A4F /* CoreDataStack.swift */; }; + DE738E6F2492DAC000C27A4F /* Task+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E6E2492DAC000C27A4F /* Task+Convenience.swift */; }; + DE738E712492DBF400C27A4F /* TaskController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E702492DBF400C27A4F /* TaskController.swift */; }; + DE738E7324930A5900C27A4F /* DateHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E7224930A5900C27A4F /* DateHelpers.swift */; }; + DE738E7524931A3000C27A4F /* ButtonTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE738E7424931A3000C27A4F /* ButtonTableViewCell.swift */; }; + DED0EF2D2495553200C8E3A9 /* TaskListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED0EF2C2495553200C8E3A9 /* TaskListTableViewController.swift */; }; FB4BD1A7237A1D88006648A7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB4BD1A6237A1D88006648A7 /* AppDelegate.swift */; }; FB4BD1A9237A1D88006648A7 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB4BD1A8237A1D88006648A7 /* SceneDelegate.swift */; }; FB4BD1AE237A1D88006648A7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FB4BD1AC237A1D88006648A7 /* Main.storyboard */; }; @@ -16,6 +23,13 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + DE738E6A2492D91900C27A4F /* TaskDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskDetailTableViewController.swift; sourceTree = ""; }; + DE738E6C2492D95E00C27A4F /* CoreDataStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; }; + DE738E6E2492DAC000C27A4F /* Task+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Convenience.swift"; sourceTree = ""; }; + DE738E702492DBF400C27A4F /* TaskController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskController.swift; sourceTree = ""; }; + DE738E7224930A5900C27A4F /* DateHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateHelpers.swift; sourceTree = ""; }; + DE738E7424931A3000C27A4F /* ButtonTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTableViewCell.swift; sourceTree = ""; }; + DED0EF2C2495553200C8E3A9 /* TaskListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskListTableViewController.swift; sourceTree = ""; }; FB4BD1A3237A1D88006648A7 /* Task.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Task.app; sourceTree = BUILT_PRODUCTS_DIR; }; FB4BD1A6237A1D88006648A7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; FB4BD1A8237A1D88006648A7 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -37,6 +51,33 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + DE738E652492D68600C27A4F /* Controllers */ = { + isa = PBXGroup; + children = ( + DE738E672492D6A600C27A4F /* ViewControllers */, + DE738E662492D69F00C27A4F /* ModelControllers */, + ); + path = Controllers; + sourceTree = ""; + }; + DE738E662492D69F00C27A4F /* ModelControllers */ = { + isa = PBXGroup; + children = ( + DE738E702492DBF400C27A4F /* TaskController.swift */, + ); + path = ModelControllers; + sourceTree = ""; + }; + DE738E672492D6A600C27A4F /* ViewControllers */ = { + isa = PBXGroup; + children = ( + DE738E6A2492D91900C27A4F /* TaskDetailTableViewController.swift */, + DE738E7424931A3000C27A4F /* ButtonTableViewCell.swift */, + DED0EF2C2495553200C8E3A9 /* TaskListTableViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; FB4BD19A237A1D88006648A7 = { isa = PBXGroup; children = ( @@ -56,6 +97,7 @@ FB4BD1A5237A1D88006648A7 /* Task */ = { isa = PBXGroup; children = ( + DE738E652492D68600C27A4F /* Controllers */, FB4BD1BE237A1E0B006648A7 /* Model */, FB4BD1BF237A1E11006648A7 /* Storyboards */, FB4BD1BD237A1E00006648A7 /* Resources */, @@ -78,6 +120,9 @@ isa = PBXGroup; children = ( FB4BD1AF237A1D88006648A7 /* Task.xcdatamodeld */, + DE738E6C2492D95E00C27A4F /* CoreDataStack.swift */, + DE738E6E2492DAC000C27A4F /* Task+Convenience.swift */, + DE738E7224930A5900C27A4F /* DateHelpers.swift */, ); path = Model; sourceTree = ""; @@ -164,7 +209,14 @@ files = ( FB4BD1B1237A1D88006648A7 /* Task.xcdatamodeld in Sources */, FB4BD1A7237A1D88006648A7 /* AppDelegate.swift in Sources */, + DE738E6F2492DAC000C27A4F /* Task+Convenience.swift in Sources */, + DE738E6D2492D95E00C27A4F /* CoreDataStack.swift in Sources */, + DED0EF2D2495553200C8E3A9 /* TaskListTableViewController.swift in Sources */, + DE738E6B2492D91900C27A4F /* TaskDetailTableViewController.swift in Sources */, + DE738E712492DBF400C27A4F /* TaskController.swift in Sources */, + DE738E7524931A3000C27A4F /* ButtonTableViewCell.swift in Sources */, FB4BD1A9237A1D88006648A7 /* SceneDelegate.swift in Sources */, + DE738E7324930A5900C27A4F /* DateHelpers.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Task/Task/Controllers/ModelControllers/TaskController.swift b/Task/Task/Controllers/ModelControllers/TaskController.swift index 940fb93..2691fa2 100644 --- a/Task/Task/Controllers/ModelControllers/TaskController.swift +++ b/Task/Task/Controllers/ModelControllers/TaskController.swift @@ -7,3 +7,67 @@ // import Foundation +import CoreData + +class TaskController { + + static let shared = TaskController() + + //Source of truth + + var fetchedResultsController: NSFetchedResultsController + + init() { + let fetchedRequest: NSFetchRequest = Task.fetchRequest() + + fetchedRequest.sortDescriptors = [NSSortDescriptor(key: "isComplete", ascending: true), NSSortDescriptor(key: "due", ascending: true)] + + + + let resultsController = NSFetchedResultsController(fetchRequest: fetchedRequest, managedObjectContext: CoreDataStack.context, sectionNameKeyPath: "isComplete", cacheName: nil) + fetchedResultsController = resultsController + do { + try fetchedResultsController.performFetch() + } catch { + print(error.localizedDescription) + } + } + + + // MARK: - CRUD Methods + + //create + func add(taskWithName name: String, notes: String?, due: Date?) { + Task(name: name, notes: notes, due: due) + saveToPersistenceStore() + } + + //update + func update(task: Task, name: String, notes: String, due: Date) { + task.name = name + task.notes = notes + task.due = due + saveToPersistenceStore() + } + + //delete + func delete(task: Task) { + task.managedObjectContext?.delete(task) + saveToPersistenceStore() + } + + func toggleIsComplete(task: Task) { + task.isComplete = !task.isComplete + saveToPersistenceStore() + } + + + + func saveToPersistenceStore() { + do { + try CoreDataStack.context.save() + } catch { + print("There was an error in \(#function): \(error) - \(error.localizedDescription)") + } + } +} diff --git a/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift b/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift index 8acfc6c..64f9e5c 100644 --- a/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift +++ b/Task/Task/Controllers/ViewControllers/ButtonTableViewCell.swift @@ -8,17 +8,31 @@ import UIKit -class ButtonTableViewCell: UITableViewCell { - - override func awakeFromNib() { - super.awakeFromNib() - // Initialization code - } +protocol ButtonTableViewCellDelegate: AnyObject { + func buttonCellButtonTapped(_ sender: ButtonTableViewCell) +} - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - // Configure the view for the selected state +class ButtonTableViewCell: UITableViewCell { + + @IBOutlet weak var primaryLabel: UILabel! + @IBOutlet weak var completeButton: UIButton! + + + + weak var delegate: ButtonTableViewCellDelegate? + + func updateButton(_ isComplete: Bool) { + let imageName = isComplete ? "complete" : "incomplete" + completeButton.setImage(UIImage(named: imageName), for: .normal) + } + + func update(withTask task: Task) { + primaryLabel.text = task.name + updateButton(task.isComplete) + } + + @IBAction func completeButtonTapped(_ sender: Any) { + delegate?.buttonCellButtonTapped(self) } - } diff --git a/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift b/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift index 3e00179..3a3a638 100644 --- a/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift +++ b/Task/Task/Controllers/ViewControllers/TaskDetailTableViewController.swift @@ -9,82 +9,72 @@ import UIKit class TaskDetailTableViewController: UITableViewController { + + @IBOutlet weak var taskNameTextField: UITextField! + @IBOutlet weak var dueTextField: UITextField! + @IBOutlet weak var noteTextView: UITextView! + @IBOutlet var dueDatePicker: UIDatePicker! + + + var task: Task? + var dueDateValue: Date? + override func viewDidLoad() { super.viewDidLoad() + updateViews() + dueTextField.inputView = dueDatePicker - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem } - - // MARK: - Table view data source - - override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections - return 0 + + //Actions + //Come back to this + @IBAction func saveButtonTapped(_ sender: Any) { + guard let name = taskNameTextField.text, !name.isEmpty, let noteText = noteTextView.text, !noteText.isEmpty, let due = dueDateValue else {return} + if let task = task { + TaskController.shared.update(task: task, name: name, notes: noteText, due: due) + } else { + TaskController.shared.add(taskWithName: name, notes: noteText, due: due) + } + navigationController?.popViewController(animated: true) } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete implementation, return the number of rows - return 0 + + + + @IBAction func cancelButtonTapped(_ sender: Any) { + navigationController?.popViewController(animated: true) } - - /* - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - - return cell + + @IBAction func datePickerChanged(_ sender: UIDatePicker) { + dueDateValue = dueDatePicker.date + dueTextField.text = dueDateValue?.stringValue() } - */ - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true + + @IBAction func userTappedView(_ sender: UITapGestureRecognizer) { + dueTextField.resignFirstResponder() } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } + + + + func updateViews() { + taskNameTextField.text = task?.name + dueTextField.text = task?.due?.stringValue() + noteTextView.text = task?.notes + self.dueDateValue = task?.due + tableView.reloadData() } - */ + - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ + // MARK: - Table view data source - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 3 } - */ - - /* - // MARK: - Navigation - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 1 } - */ } diff --git a/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift b/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift index f6d7c21..903978d 100644 --- a/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift +++ b/Task/Task/Controllers/ViewControllers/TaskListTableViewController.swift @@ -7,84 +7,123 @@ // import UIKit +import CoreData class TaskListTableViewController: UITableViewController { + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.reloadData() + } override func viewDidLoad() { super.viewDidLoad() + TaskController.shared.fetchedResultsController.delegate = self - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections - return 0 + return TaskController.shared.fetchedResultsController.sections?.count ?? 0 + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return TaskController.shared.fetchedResultsController.sections?[section].name == "1" ? "Complete" : "Incomplete" } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows - return 0 + return TaskController.shared.fetchedResultsController.sections?[section].numberOfObjects ?? 0 } - /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - + guard let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as? ButtonTableViewCell else {return UITableViewCell()} + let task = TaskController.shared.fetchedResultsController.object(at: indexPath) + cell.update(withTask: task) + cell.delegate = self return cell } - */ - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* + + // Override to support editing the table view. override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } + let task = TaskController.shared.fetchedResultsController.object(at: indexPath) + TaskController.shared.delete(task: task) + //tableView.deleteRows(at: [indexPath], with: .fade) + } } - */ + - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + // MARK: - Navigation + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "toTaskDetail" { + guard let indexPath = tableView.indexPathForSelectedRow, let destination = segue.destination as? TaskDetailTableViewController else {return} + let taskToSend = TaskController.shared.fetchedResultsController.object(at: indexPath) + destination.task = taskToSend + } } - */ +} - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true +extension TaskListTableViewController: ButtonTableViewCellDelegate { + func buttonCellButtonTapped(_ sender: ButtonTableViewCell) { + guard let indexPath = tableView.indexPath(for: sender) else {return} + let task = TaskController.shared.fetchedResultsController.object(at: indexPath) + TaskController.shared.toggleIsComplete(task: task) + sender.update(withTask: task) } - */ +} - /* - // MARK: - Navigation - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. +extension TaskListTableViewController: NSFetchedResultsControllerDelegate { + + func controllerWillChangeContent(_ controller: NSFetchedResultsController) { + self.tableView.beginUpdates() + } + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + self.tableView.endUpdates() + } + + func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + switch type { + case .insert: + guard let newIndexPath = newIndexPath else {return} + tableView.insertRows(at: [newIndexPath], with: .fade) + case .delete: + guard let indexPath = indexPath else {return} + tableView.deleteRows(at: [indexPath], with: .fade) + case .move: + guard let newIndexPath = newIndexPath, let indexPath = indexPath else {return} + tableView.moveRow(at: indexPath, to: newIndexPath) + case .update: + guard let indexPath = indexPath else {return} + tableView.reloadRows(at: [indexPath], with: .fade) + @unknown default: + fatalError() + } + } + func controller(_ controller: NSFetchedResultsController, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { + + switch type { + case .insert: + tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade) + case .delete: + tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade) + case .move: + return + case .update: + break + @unknown default: + fatalError() + } } - */ + } diff --git a/Task/Task/Model/CoreDataStack.swift b/Task/Task/Model/CoreDataStack.swift index 32397e5..e68c415 100644 --- a/Task/Task/Model/CoreDataStack.swift +++ b/Task/Task/Model/CoreDataStack.swift @@ -7,3 +7,19 @@ // import Foundation +import CoreData + +enum CoreDataStack { + static let container: NSPersistentContainer = { + let container = NSPersistentContainer(name: "Task") //<--- Change to name of app + container.loadPersistentStores { (_, error) in + if let error = error { + fatalError("\(error.localizedDescription)") + } + } + return container + }() + static var context: NSManagedObjectContext { + return container.viewContext + } +} diff --git a/Task/Task/Model/DateHelpers.swift b/Task/Task/Model/DateHelpers.swift index e5cd421..6d23f2c 100644 --- a/Task/Task/Model/DateHelpers.swift +++ b/Task/Task/Model/DateHelpers.swift @@ -7,3 +7,11 @@ // import Foundation + +extension Date { + func stringValue() -> String { + let formatter = DateFormatter() + formatter.dateStyle = .medium + return formatter.string(from: self) + } +} diff --git a/Task/Task/Model/Task+Convenience.swift b/Task/Task/Model/Task+Convenience.swift index 35d1700..fa22517 100644 --- a/Task/Task/Model/Task+Convenience.swift +++ b/Task/Task/Model/Task+Convenience.swift @@ -7,3 +7,14 @@ // import Foundation +import CoreData + +extension Task { + @discardableResult + convenience init (name: String, notes: String?, due: Date?) { + self.init(context: CoreDataStack.context) + self.name = name + self.notes = notes + self.due = due + } +} diff --git a/Task/Task/Model/Task.xcdatamodeld/Task.xcdatamodel/contents b/Task/Task/Model/Task.xcdatamodeld/Task.xcdatamodel/contents index 800fc6f..9111c25 100644 --- a/Task/Task/Model/Task.xcdatamodeld/Task.xcdatamodel/contents +++ b/Task/Task/Model/Task.xcdatamodeld/Task.xcdatamodel/contents @@ -1,7 +1,12 @@ - - + + + + + + + - + \ No newline at end of file diff --git a/Task/Task/Storyboards/Base.lproj/Main.storyboard b/Task/Task/Storyboards/Base.lproj/Main.storyboard index 79c6055..8485b91 100644 --- a/Task/Task/Storyboards/Base.lproj/Main.storyboard +++ b/Task/Task/Storyboards/Base.lproj/Main.storyboard @@ -1,8 +1,237 @@ - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +