For the second midterm exam I asked you to write an app to allow the user to create and view a list of tasks. For the final exam we are going create a SwiftUI app that implements a slightly fancier todo list manager.
The app will have a tabbed interface, with a View tab and a Create tab.
The Create tab will display a single view that the user can use to create new tasks for their calendar.
The View tab will contain a navigation view with a couple of views in it. The first view is a summary of events for the next week. Each entry in the list is a combination of a date and the number of ToDos currently recorded for that date.
Clicking on any one of the items in the summary takes the user to a second view where they can see the list of tasks for that date.
The user can swipe left on any item in this list to delete the task from their list of ToDos.
Here is the complete code for the app's model class. This class stores all of the ToDo objects and manages reading them from and writing them to a local file.
import Foundation class ToDo : Codable, Equatable, Identifiable { static func == (lhs: ToDo, rhs: ToDo) -> Bool { let cal = Calendar.current return lhs.todo == rhs.todo && cal.isDate(lhs.date,inSameDayAs:rhs.date) } var todo : String var date : Date var id = UUID() init() { todo = "" date = Date.now } init(text str : String,time tm : Date) { todo = str date = tm } } class Summary { var date : Date var dateName : String var count : Int init(_ start : Date,offset o : Int) { date = Calendar.current.date(byAdding: .day, value: o, to: start)! let formatter = DateFormatter() formatter.setLocalizedDateFormatFromTemplate("EEE MMM d") dateName = formatter.string(from: date) count = 0 } } class AppData : ObservableObject { var todos : [ToDo] @Published var today : [ToDo] @Published var week : [Summary] @Published var selectedDate : String = "" let itemArchiveURL: URL = { let documentsDirectories = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let documentDirectory = documentsDirectories.first! return documentDirectory.appendingPathComponent("todos.plist") }() init() { today = [] week = [] do { let data = try Data(contentsOf: itemArchiveURL) let unarchiver = PropertyListDecoder() let list = try unarchiver.decode([ToDo].self, from: data) todos = list } catch { todos = [] } buildSummary() } func buildSummary() { let start = Date.now let cal = Calendar.current var w : [Summary] = [] for o in 0..<7 { let s = Summary(start,offset:o) var c = 0 for td in todos { if cal.isDate(td.date, inSameDayAs: s.date) { c += 1 } } s.count = c w.append(s) } week = w } func filter(_ s : Summary) { selectedDate = s.dateName today = [] let cal = Calendar.current for td in todos { if cal.isDate(td.date, inSameDayAs: s.date) { today.append(td) } } } func addTask(_ td : ToDo) { todos.append(td) buildSummary() } func removeTask(_ rem : ToDo) { today.removeAll(where: { td in td == rem }) todos.removeAll(where: { td in td == rem }) buildSummary() } func saveChanges() { let encoder = PropertyListEncoder() let data = try! encoder.encode(todos) try! data.write(to: itemArchiveURL) } }
The model class publishes two lists. The array week
is an array of Summary objects for the upcoming week. You will display the contents of this array in the first view in the View tab group. In that list you will display the dateName
and the count
for each Summary object in the week list. The array today
displays a list of ToDo objects for a particular date. You will display that list in the second view in the View tab group. To construct the today list for a particular date, call the filter()
method and pass it a Summary object for the day you want to display.
When the user clicks the Create button in the view for creating new ToDos you should call the addTask()
method in the model to add the new task, and then also call saveChanges()
to save your changes out to the local file.
To remove a task from both the today list and the list of all ToDos, call the removeTask()
method, and then call saveChanges()
to save your changes to the file.
In the view for creating new ToDo objects you will need to display a date picker. Here is the code you should use to set up that date picker:
DatePicker("",selection: $date,displayedComponents: .date)
Here date
is a state variable of type Date.