Hi fellow developers,
I have an app in its early stages utilising Core Data.
The premise is simple: Add in restaurants, save it to Core Data and display it. So I have an AddEntry
with a bunch of textFields
- the user fills those in, clicks Save
and it gets saved to Core Data. In the main view, the data gets displayed using NSFetchedResultsController
and UITableViewController
. That works well.
I have a DetailView
which looks exactly like the AddEntry
and is a segue away from the Timeline
; click a cell and it goes to a DetailView
displaying all of the information populated in.
I'm trying to edit entry here, by for example, changing the name of the food, or the restaurant (textFields
) and save it to Core Data. Saving works, but what I've realised is that it actually doesn't override the entry, it just creates a brand new entry.
I totally understand why it's doing that - I'm creating a new instance, etc, but I'm not 100% sure what to do.
In a previous app, I used a technique to:
- perform a fetch request
- create an array of fetchRequets
- if the entry doesn't exist, create it
- if it does, return it.
That code (written in Objective C) is below:
Person *person = nil;
// Creating a fetch request to check whether the person already exists, calling from the Add/DetailViewController.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
request.predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *people = [context executeFetchRequest:request error:&error];
if (!people)
{
// Handle Error
}
else if (![people count])
{
// If the person count is 0 then create it
person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
person.name = name;
}
else
{
// If the object exists, just return it
person = [people lastObject];
}
return person;
I'm now doing this new application in Swift, and I'm wondering:
- is this the right approach? To fetch request, create new entry if it doesn't exist, or return it if it does?
- In my Save code, I don't seem to have "
insertNewObjectForEntityForName
" as part of my new Swift code - am I missing that?
Here's my "save" code.
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
// I am creating an instance of both the food and restaurant from the Food and Restaurant Managed Objects.
foodEntry = FoodManagedObject(context: appDelegate.persistentContainer.viewContext)
restaurantEntry = RestaurantsManagedObject(context: appDelegate.persistentContainer.viewContext)
foodEntry.nameOfFood = foodTextField.text
appDelegate.saveContext()
print("the food name is \(String(describing: foodEntry.nameOfFood))")
}
Any thoughts are welcomed.
Update
The answer provided the information I needed to get this working, but for my own understanding, I need to figure out something and see if I can understand what's going on.
In the video provided and the other solutions, when adding a new entry to CoreData, the code insertNewObject(forEntity: "Entity" into: managedObjectContext)
is provided.
In my application, the code below does the job of creating a new entry in CoreData (I can see because the NSFetchedResultsController
provides the results). Why does my code work, what is my code actually doing differently from insertNewObject(forEntity: "Entity" into: managedObjectContext)
and should I actually just be using insertNewObject(forEntity: "Entity" into: managedObjectContext)?
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
// I am creating an instance of both the food and restaurant from the Food and Restaurant Managed Objects.
foodEntry = FoodManagedObject(context: appDelegate.persistentContainer.viewContext)
restaurantEntry = RestaurantsManagedObject(context: appDelegate.persistentContainer.viewContext)
// I am firstly setting the name to the nameTextField. That's easy.
foodEntry.nameOfFood = foodNameTextField.text
foodEntry.type = restaurantTypeTextField.text
// I am then setting the restaurant Name using the restaurnt Managed Object and then assigning the foodEntry.restaurant (the relationship) to the entry. This is a trick from Envylope and is what got the restaurantName to display on the Timeline. The same is done for the address.
restaurantEntry.nameOfRestaurant = restaurantNameTextField.text
restaurantEntry.address = restaurantLocationTextField.text
foodEntry.restaurant = restaurantEntry