The first model you will persist is the family member model. The app is already set up with a form that asks for a family member name and a delegate protocol that informs FamilyMembersViewController whenever the user wants to store a new family member.
Note that none of the input data is validated; usually, you'd want to add some checks that make sure that the user is not trying to insert an empty family member name, for instance. For now, we'll skip that because this type of validation isn't Core-Data-specific.
The code to persist new family members should be added to the saveFamilyMember(withName:) method. Add the following implementation to FamilyMembersViewController; we'll go over it line by line after adding the code:
func saveFamilyMember(withName name: String) { // 1 let moc = persistentContainer.viewContext // 2 moc.perform { // 3 let familyMember = FamilyMember(context: moc) familyMember.name = name // 4 do { try moc.save() } catch { moc.rollback() } } }
The first comment in this code marks where the managed object context is extracted from persistentContainer. All NSPersistentContainer objects have a viewContext property. This property is used to obtain a managed object context that exists on the main thread.
The second comment marks the call to perform(_:). This ensures that the new FamilyMember instance is created and stored on the correct thread. When you create an instance of a managed object context, you must pass the managed object context to the instance of NSManagedObject.
Lastly, saving the managed object context can fail, so you must wrap the call to save() in a do {} catch {} block, so it correctly handles potential errors. If the managed object context can't be saved, all unsaved changes are rolled back.
This code is all you need to persist family members. Before you implement the required code to read existing family members and respond to the insertion of new family members, let's set up MoviesViewController so it can store movies for a family member.
The code to store movies for a family member is very similar to the code you wrote earlier. Before you implement the following snippets, make sure that you conform MoviesViewController to PersistenContainerRequiring and import CoreData. Also, add a persistentContainer property to MoviesViewController.
In order to connect a new movie to a family member, you also need a variable to hold the family member in MoviesViewController. Add the following declaration to MoviesViewController:
var familyMember: FamilyMember?
After doing this, add the following implementation for saveMovie(withName:):
func saveMovie(withName name: String) { guard let familyMember = self.familyMember else { return } let moc = persistentContainer.viewContext moc.perform { let movie = Movie(context: moc) movie.title = name // 1 let newFavorites: Set<AnyHashable> = familyMember.movies?.adding(movie) ?? [movie] // 2 familyMember.movies = NSSet(set: newFavorites) do { try moc.save() } catch { moc.rollback() } } }
The most important differences between adding the movie and the family member are highlighted with comments. Note that the movies property on a family member is NSSet. This is an immutable object so you need to create a copy and add the movie to that copy. If no copy could be made because there is no set created yet, you can create a new set with the new movie in it. Next, this new updated Set is converted back to an NSSet so it can be the new value for movies.
As you can see, both save methods share about half of the implementation. You can make some clever use of extensions and generics in Swift to avoid writing this duplicated code. Let's refactor the app a bit.