I have a UITableViewController that displays a list of lift names (think weightlifting) and it's used for two different things. It can be accessed from view A to select a lift that will be saved as a user default. It can also be accessed from view B to select a lift that will then filter a list of lift events. In each case, I want a checkmark on the appropriate cell to indicate the current default lift (if coming from view A) and a checkmark on the appropriate cell to indicate the currently selected filter (if coming from view B).
I created a view model for the table cells:
struct LiftCellViewModel: SelectionsRepresentable {
let liftName: String!
let liftsDictionary: [String: String]!
var text: String {
return liftName
}
var accessoryType: UITableViewCellAccessoryType {
let defaultLiftUuid = UserDefaults.lift()
let defaultLiftName = liftsDictionary[defaultLiftUuid]
if liftName == defaultLiftName {
return .checkmark
} else {
return .none
}
}
}
This works very, but only handles one of the scenarios (coming from view A). So, to try and get it to also work with the other scenario (coming from view B) I'm passing in a bool if the list is being used to select a filter and using that in a switch statement to determine what path to take in order to set the checkmark:
let liftName: String!
let liftsDictionary: [String: String]!
let selectForFilter: Bool!
var text: String {
return liftName
}
var accessoryType: UITableViewCellAccessoryType {
switch selectForFilter {
case true:
let filterLiftUuid = UserDefaults.logFilterLiftUuid()
let filterLiftName = liftsDictionary[filterLiftUuid!]
if liftName == filterLiftName {
return .checkmark
} else {
return .none
}
case false:
let defaultLiftUuid = UserDefaults.lift()
let defaultLiftName = liftsDictionary[defaultLiftUuid]
if liftName == defaultLiftName {
return .checkmark
} else {
return .none
}
default:
return .none
}
}
This view is used to create each cell in didSelectRow:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellViewModel: SelectionsRepresentable
let cell = tableView.dequeueReusableCell(withIdentifier: "liftCell", for: indexPath)
let lift = viewModel.fetchedResultsController?.object(at: indexPath) as! Lift
let liftName = lift.liftName
cellViewModel = LiftCellViewModel(liftName: liftName, liftsDictionary: liftsDictionary, selectForFilter: selectForFilter)
cell.textLabel?.text = cellViewModel.text
cell.accessoryType = cellViewModel.accessoryType
return cell
}
This works but the switch
statement seems like a lot of code to do something so simple. Is there a Swiftier way to do this?
If you look at your definition of accessoryType
, really the only difference is which UUID you retrieve so this could be simplified to something like the following:
var accessoryType: UITableViewCellAccessoryType {
let uuid = selectForFilter! ? UserDefaults.logFilterLiftUuid() : UserDefaults.lift()
return liftName == liftsDictionary[uuid!] ? .checkmark : .none
}
Speaking of swifty, you might also want to rethink your ample use of implicitly unwrapped optionals and use non-optional types instead where possible.