Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a calculated property in Core Data

I have a core data model that's hooked up to a Storyboard GUI.(Xcode 9 and SWIFT 4) I now want to add a property which is derived from two others. I have an Entity called Workout and two of it's properties "rpe" and "seconds" are combined to create a new property "rpeTSS". I have declared "repTSS" as an attribute in my core data model. I've set it to transient.

I have tried using the "make NSManagedObject subclass..." option under the Editor menu but this seemed to produce files with errors. I tried subclassing the generated Workout class but couldn't figure out how to bind that in to the GUI.

I then made an extension to Workout:

extension Workout{

    public override func value(forKey key: String) -> Any? {
        if key == "rpeTSS"{
            return (100/49)*rpe*rpe*Double(seconds)/3600
        }else{return super.value(forKey: key)}
    }
}

I'll be honest this doesn't 'feel' right. Anyway it (kind of) works. I do now get the correct value for rpeTSS and it shows up in my gui. However if I make changes to rpe and/or seconds the GUI doesn't update.

I'm guessing the method above is a bit of a hack and thus circumvents various messaging that gets the GUI to update.

What is the correct way to add derived properties within core data? I'm trying to achieve this in a way that means if I add anything to my data model I don't have to go re-writing of the code for the derived properties.

like image 511
Steven Lord Avatar asked Oct 05 '17 19:10

Steven Lord


2 Answers

Still not sure this is the right way to do it but it does solve the question as asked. However I would be interested if there is a 'right' way to do what I'm trying.

I managed to get it all to update by overriding the setValue(: forKey) method in my extension:

extension Workout{

    public override func value(forKey key: String) -> Any? {
        if key == "rpeTSS"{
            if rpeTSS == 0{
                self.rpeTSS = (100/49)*rpe*rpe*Double(seconds)/3600
            }
        return rpeTSS
        }
        return super.value(forKey: key)
    }

    public override func setValue(_ value: Any?, forKey key: String) {
        super.setValue(value, forKey: key)
        if (key == "rpe") || (key == "seconds") {
            self.rpeTSS = calculateRPETSS()
        }
    }

    func calculateRPETSS() -> Double{
        return (100/49)*rpe*rpe*Double(seconds)/3600
    }
}
like image 165
Steven Lord Avatar answered Oct 09 '22 08:10

Steven Lord


Not sure if this is what you mean, but...

I have an Entity class called Person. Here are my two classes:

Person+CoreDataProperties.swift:

extension Person {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Total> {
    return NSFetchRequest<Person>(entityName: "Person")
}

@NSManaged public var name: String?
@NSManaged public var age: Int

}

Person+CoreDataClass.swift:

@objc(Person)
public class Total: NSManagedObject {

}

These were generated by Xcode. And then in my ViewController when I want to use this class I use:

var curPerson: Person?
let personEntity = NSEntityDescription.entity(forEntityName: "Person", in: Singleton.s.context)!

Singleton.s.people = try Singleton.s.context.fetch(Person.fetchRequest())

// find the current person
for p in Singleton.s.people {
    if p.name == curName { // got it
        curPerson = p
        break
    }
}

Now I have a Person object to manipulate. To make a new object I use:

let newPerson = Person(entity: personEntity, insertInto: Singleton.s.context)

And to make sure my changes are saved I use:

(UIApplication.shared.delegate as! AppDelegate).saveContext()

like image 1
CodeNinja Avatar answered Oct 09 '22 06:10

CodeNinja