Search code examples
xcodeswiftnsfetchedresultscontrollerrelationshipsmanagedobjectcontext

Change Value of Attribute Through Core Data Relationship


I'm struggling with updating a value stored in an entity that has a "one-to-many" relationship with another entity.

My project is a budgeting app. Using NSFetchedResultsController, I can successfully add a transaction (which populates in the table) and delete transactions which the FRC automatically saves.

I have an entity that stores the transactions with attributes "name" and "amount", and I have a separate entity with one attribute "theDate" (NSDate) that stores the date of the transaction.

The reason it is separate is because I believed this would organizationally make it simpler-many transactions can occur on the same date.

In the below I create the entities and store the values after creating a new transaction, then set the relationship:

 @IBAction func done(segue: UIStoryboardSegue) {
    let addTransactionVC = segue.sourceViewController as! AddTransaction
    let newTrans = addTransactionVC.newTransaction
    let date = addTransactionVC.datePicker.date

    // Create Entities
    let entity = NSEntityDescription.entityForName("DailyTransactions", inManagedObjectContext: self.managedContext)
    let relatedEntity = NSEntityDescription.entityForName("TransDates", inManagedObjectContext: self.managedContext)

    // Initialize Record
    let record = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.managedContext)
    let recordDate = NSManagedObject(entity: relatedEntity!, insertIntoManagedObjectContext: self.managedContext)

    // Is new transaction being created or an old one being edited?
    if !addTransactionVC.isEditingTransaction {

        // Populate Record
        record.setValue(newTrans.name, forKey: "transName")
        record.setValue(newTrans.amount, forKey: "transAmount")
        recordDate.setValue(date, forKey: "theDate")

        // Create Relationship of Date to Transaction
        record.setValue(recordDate, forKey: "date")

    } else {

        // If user was editing a transaction:
        let updatedObject = addTransactionVC.managedObject
        let newDate = addTransactionVC.datePicker.date

        // How do I change the date associated with this transaction?
        updatedObject.setValue(newTrans.name, forKey: "transName")
        updatedObject.setValue(newTrans.amount, forKey: "transAmount")

        // This line was meant to access the attribute in the "date" relationship:
        updatedObject.setValue(newDate, forKey: "date")

Everything above else works fine. After else triggers if the cell (transaction) was selected to be edited. This line: updatedObject.setValue(newDate, forKey: "date") was meant to simply update the "theDate" attribute of the TransDates entity ("date" is the name of the relationship). But I see now why that won't work.

So I tried this in the else statement:

        let fetchRequestDates = NSFetchRequest(entityName: "TransDates")

        do {

            let dateObjects = try self.managedContext.executeFetchRequest(fetchRequestDates) as! [TransDates]
            for dateObject in dateObjects {

                // These 2 lines were to test that a date was being returned:
                let tempDate = dateObject.theDate as! NSDate
                print("dateObject is:\n\(String(tempDate))")

                if dateObject.valueForKey("theDate") as! NSDate == newDate {
                    updatedObject.setValue(dateObject, forKey: "date")
                    break
                }
            }

        } catch let error as NSError {
            print("Could not fetch \(error), \(error.userInfo)")
        }

I thought the above would simply return an array of all the TransDates objects and if a match was found (b/w what's in the array and the new NSDate) the updatedObject would get added to it, otherwise I would add some code to create the newDate as a new NSManagedObject, etc.

However when executing if dateObject.valueForKey("theDate") as! NSDate == newDate { this happens: "fatal error: unexpectedly found nil while unwrapping an Optional value".

My two questions:

  1. In terms of what I'm trying to accomplish-update/change a date that's associated with a transaction (if the date already exists and contains other transactions, this transaction just moves over to join them), is this the best way to do it?

  2. Do I have the purpose/functionality of relationships all wrong?

Sorry for this long-winded question, but I've been stuck here for days. So thanks in advance!


Solution

  • The reason you are getting this error is because you are trying to force unwrap a nil value. You should instead use an if let statement:

    if let date dateObject.valueForKey("theDate") as? NSDate {
        if date == newDate {
            ...
        }
    }