I have a UITableView
in my app that shows a long list of NSManagedObjects
of type LiftEvent
that are in an NSFetchedResultsController
. One of the values displayed, however, isn't a stored property, it's calculated by calling a method on the object when the view model for the UITableViewCell
is being created (in the extension
):
class LiftLogTableViewCell: UITableViewCell {
@IBOutlet weak var liftName: UILabel!
@IBOutlet weak var liftDetails: UILabel!
@IBOutlet weak var liftDate: UILabel!
@IBOutlet weak var oneRepMaxWeight: UILabel!
@IBOutlet weak var unit: UILabel!
@IBOutlet weak var formula: UILabel!
var liftWeight: String!
var repetitions: String!
struct ViewData {
let liftName: String
let liftWeight: Double
let repetitions: Int
let liftDate: String
let oneRepMaxWeight: Double
let unit: String
let formula: String
}
var viewData: ViewData! {
didSet {
liftName!.text = viewData.liftName
liftWeight = String(viewData.liftWeight)
repetitions = String(viewData.repetitions)
let formatter = StringFormatter()
let formattedWeightLifted = formatter.formatForDisplay(viewData.liftWeight)
liftDetails!.text = "\(formattedWeightLifted) @ \(viewData.repetitions)"
liftDate!.text = "on \(viewData.liftDate)"
oneRepMaxWeight!.text = "\(viewData.oneRepMaxWeight)"
unit!.text = viewData.unit
formula!.text = viewData.formula
}
}
}
extension LiftLogTableViewCell.ViewData {
init(liftEvent: LiftEvent) {
self.liftName = liftEvent.lift.liftName
// liftEvent.calculatOneRepMax() uses two stored properties of the LiftEvent to calculate the value
let oneRepMax = liftEvent.calculateOneRepMax()
if liftEvent.liftWeight.unit.symbol == UserDefaults.weightUnit() {
self.liftWeight = liftEvent.liftWeight.value
self.oneRepMaxWeight = oneRepMax.value
self.unit = oneRepMax.unit.symbol
} else {
let convertedLiftWeight = ConvertWeightService().convertToOtherUnit(weight: liftEvent.liftWeight)
self.liftWeight = convertedLiftWeight.value
let convertedMaxWeight = ConvertWeightService().convertToOtherUnit(weight: oneRepMax)
self.oneRepMaxWeight = convertedMaxWeight.value
self.unit = convertedMaxWeight.unit.symbol
}
self.repetitions = Int(liftEvent.repetitions)
self.formula = liftEvent.formula.formulaName
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let formattedDate = dateFormatter.string(from: liftEvent.date as Date)
self.liftDate = "\(formattedDate)"
}
}
I've done my homework found and a thread from two years ago and another thread from over four years ago that say it can't be done when using an NSFecthedResultsController
, but Swift is changing so rapidly that I wonder if it might be possible now. I've read Apple's documentation and I couldn't see a way to do it but I'm pretty new to this API.
Is there a way to do this with an NSFetchedResultsController
or do I have to give that up and use a simple array?
There is nothing magical about swift that will allow you to create a fetchedResultsController sorted by properties not stored in core data. There are three possible solutions:
date
and then display by day
even though day
is not a property it is a derived property from date
. You can even use day
as a sectionNameKeyPath
.1 is the best solution, but I don't understand your model to know if it is possible. If it is not possible I would recommend 2. In general it is a good practice to update your model from only one place so adding a code to sync your derived properties should not be so onerous.