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:
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?
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!
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: