Mochi Mochi - 2 months ago 8
Swift Question

How to handle empty objects with Swift?

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?

Answer

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.