Reputation: 2524
In my App I am using Core Data and up to this point everything works great. I am having a problem when I try to update an entry. What is interesting is that I can update everything in the entity except for the address field. I don't do anything different but the app crashes when I try to update only that field with a fatal error: Index out of range. Here is the screen shot of the actual entity.
It is just some client information and all entries are strings. In my edit vc I have used a var to hold the client info, set the delegate and used some predicates. Here is the code:
//This is declared right under the class
var myClientToEdit: NSManagedObject = NSManagedObject()
//my save button action
@IBAction func saveMyEdits(_ sender: Any)
{
//get the delegate
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else
{
return
}
//get managedContext
let managedContext = appDelegate.persistentContainer.viewContext
//get the client's entry
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Clients")
fetchRequest.returnsObjectsAsFaults = false
//use predicates to make sure it is the client I need
fetchRequest.predicate = NSPredicate(format: "lastName == %@", mylastName)
fetchRequest.predicate = NSPredicate(format: "firstName == %@", myfirstName)
fetchRequest.predicate = NSPredicate(format: "address = %@", myaddress)
do {
var myClientToEdit = try managedContext.fetch(fetchRequest)
let myObject = myClientToEdit[0]
//my correct client is printed
print(myObject)
//using new entries to set new values
myObject.setValue(lastName.text!, forKey: "lastName")
myObject.setValue(firstName.text!, forKey: "firstName")
myObject.setValue(address.text!, forKey: "address")
myObject.setValue(city.text!, forKey: "city")
myObject.setValue(state.text!, forKey: "state")
myObject.setValue(zip.text!, forKey: "zipCode")
myObject.setValue(phoneDay.text!, forKey: "phoneDay")
myObject.setValue(phoneEve.text!, forKey: "phoneEvening")
myObject.setValue(email.text!, forKey: "email")
do{
//save
try managedContext.save()
}catch let error as NSError{
print(error)
}
}catch let error as NSError {
print(error)
}
//dismis upon saving edits
self.dismiss(animated: true, completion: nil)
}
When I come back to my detail client vc I do the same thing but just read from Core Data. Here is the code for the detailed client vc.
override func viewWillAppear(_ animated: Bool)
{
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else
{
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Clients")
fetchRequest.predicate = NSPredicate(format: "lastName == %@", myLastName)
fetchRequest.predicate = NSPredicate(format: "firstName == %@", myFirstName)
fetchRequest.predicate = NSPredicate(format: "address == %@", myAddress)
do {
var thisIsTheClient = try managedContext.fetch(fetchRequest)
let myObject = thisIsTheClient[0]
print(myObject)
lastNameLabel.text = myObject.value(forKey: "lastName") as? String
firstNameLabel.text = myObject.value(forKey: "firstName") as? String
myCityLabel.text = myObject.value(forKey: "city") as? String
myAddressLabel.text = myObject.value(forKey: "address") as? String
myStateLabel.text = myObject.value(forKey: "state") as? String
myZipLabel.text = myObject.value(forKey: "zipCode") as? String
myDayLabel.text = myObject.value(forKey: "phoneDay") as? String
myEveLabel.text = myObject.value(forKey: "phoneEvening") as? String
myEmailLabel.text = myObject.value(forKey: "email") as? String
}catch let error as NSError
{
print(error)
}
}
The error is on the line: let myObject = thisIsTheClient[0]. BUT only when I try and edit the address. thisIsTheClient is defined under the class as well. I know index out of range has something to do with trying to put something where there is nothing like in an array, but I don't understand why it only does that when I try and edit the address. All other entries are updated perfectly.
EDIT 7/19/17
Thanks for everyone's responses! Both Maor, and Jay had the predicate thing right. I was only searching for the address on the client, not the first three attributes. And after Jay's note about the var, I was able to pass the new data back and upadate the var and search for the new record. Thanks again!
Upvotes: 1
Views: 749
Reputation: 265
I'm fairly sure in order to combine predicates you have to use a compound predicate, instead of just trying to change the property of the fetch request:
let predicate1:NSPredicate = NSPredicate("label = 'foo'")
let predicate2:NSPredicate = NSPredicate("label = 'bar'")
let compound:NSCompoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2])
Upvotes: 1
Reputation: 146
I think you have a different problem than what you think it is. First, when you write:
fetchRequest.predicate = NSPredicate(format: "lastName == %@", myLastName)
fetchRequest.predicate = NSPredicate(format: "firstName == %@", myFirstName)
fetchRequest.predicate = NSPredicate(format: "address == %@", myAddress)
Only the last line is taking into account since it is overrides whatever comes before it, which means that in your case it will only look for the address attribute (and I guess this why you believed that your problem is only in the address field..) You should follow what @Jay offered you to do to have a compound predicates.
The second thing (if I understand correctly), is that you changed the address to whatever text myAddressLabel.text holds, and saved it. However when you read from core data on detail vc, you still try to query by "myAdress" variable, which is different than the one you saved. And if that is the case than it explains why you get this error message- it is simply didn't find anything matches your search, so when you try to get the first element out of an empty array it tells you that it is out of bounds...
Upvotes: 1