I have an app that keeps track of monthly expenses. I have an Expense entity with a month attribute that keeps track of current month expense is created on. I would then display the expenses for each month in a table view as shown here. The user can only switch left and right only if there are expenses within the next or the last month
@IBAction func backMonthButtonPressed(sender: AnyObject) {
print("Back Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
if checkMonth(currentMonth) {
updateFetch()
setMonth()
} else {
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
}
tableView.reloadData()
}
@IBAction func nextMonthButtonPressed(sender: AnyObject) {
print("Next Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
if checkMonth(currentMonth) {
updateFetch()
setMonth()
} else {
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
}
tableView.reloadData()
}
func checkMonth(month : NSDate) -> Bool {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
let fetchRequest = NSFetchRequest(entityName: "Expense")
fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
let count = context.countForFetchRequest(fetchRequest, error: nil)
if count > 0 {
print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
return true
} else {
print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
return false
}
}
My problem is this, in the unlikely scenario that the user created an expense back in June and didn't create an expense in August. How can I let the user still see his/her expense back in August without skipping it. Any ideas?
I made some optimisation before elaboration:
@IBAction func backMonthButtonPressed(sender: AnyObject) {
self.processMonth(step: -1)
}
@IBAction func nextMonthButtonPressed(sender: AnyObject) {
self.processMonth(step: 1)
}
// a method uses almost the same code for both cases, so it was merged
func processMonth(step: Int) {
let direction = (step < 1 ? "Back" : "Next")
print("\(direction) Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: step, toDate: currentMonth, options: [])!
//if checkMonth(currentMonth) {
// I wouldn't test this because it locks you out from seeing empty month.
updateFetch()
setMonth()
//}
tableView.reloadData()
}
An answer to what you've exactly asked:
If your data source for your UITableView is set properly, you should be able to go through empty months though
// changed return type from `Bool` to `void` as I suggested in the method not to test empty month, as it could be useless
func checkMonth(month : NSDate) {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
let fetchRequest = NSFetchRequest(entityName: "Expense")
fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
let count = context.countForFetchRequest(fetchRequest, error: nil)
if count > 0 {
print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
} else {
print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
// here you can make some additional actions, like seeting the empty table with "no expenses this month"
}
}
Anyway, as @Paulw11 noted, if your data source is not size-exhausting, you could rather fetch the data from your data-model at viewDidLoad/viewDidAppear for example and then to render each month according to the currentMonth
variable (regarding what month a user currently see).
So as a result, you would call setMonth()
method only in the load of your controller and each time a user changes a current month view.