Updating the data model

The data model we currently have associates movies with a single family member. This means that the app potentially stores the same movie over and over again. When we were only storing data, this wasn't that big of a deal. However, now that we will query the movie database in a background fetch task, we need this task to be as fast as we possibly can make it. This means that we don't want to ask the server for the same movie twice. Also, we most certainly don't want to use the search API as we did before; we want to be as specific about a movie as we can.

To facilitate this, we must change the relationship between movies and family members to a many-to-many relationship. We'll also add a new field to the movie entity: remoteId. This remoteId will hold the identifier the movie database uses for the particular movie so we can use it directly in later API calls.

Open the model editor in Xcode and add the new property to Movie. Make sure that it's a 64-bit integer and that it's optional. Also, select the familyMember relationship and change it to a To Many relationship in the sidebar. It's also a good idea to rename the relationship to familyMembers since we're now relating it to more than one family member as shown in the following screenshot:

Great, the model has been updated. We still need to perform a bit of work though. Because we changed the name and nature of the family member relationship, our code won't compile. Make the following modifications to the managedObjectContextDidChange(_:) method in MoviesViewController.swift; the modified lines are highlighted:

if let updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set<Movie> { 
for object in updatedObjects {
if let familyMember = self.familyMember,
let familyMembers = object.familyMembers,
familyMembers.contains(familyMember) {


tableView.reloadData()
break
}
}
}

There is just one more model-related change that we will need to incorporate. In order to efficiently search for an existing movie or create a new one, we will add an extension to the Movie model. Create a new group called Models and add a new Swift file named Movie.swift to it. Finally, add the following implementation to the file:

import CoreData 

extension Movie {
static func find(byName name: String, orCreateIn moc: NSManagedObjectContext) -> Movie {
let predicate = NSPredicate(format: "name ==[dc] %@", name)
let request: NSFetchRequest<Movie> = Movie.fetchRequest()
request.predicate = predicate

guard let result = try? moc.fetch(request)
else { return Movie(context: moc) }

return result.first ?? Movie(context: moc)
}
}

The preceding code queries CoreData for an existing movie with the same name. The movies are matched case-insensitive by passing [dc] to the == operator. This lookup is efficient because people might write the same movie name with different capitalization. If we aren't able to find a result, or if the results come back empty, a new movie is created. Otherwise, we return the first and presumably the only result CoreData has for our query. This wraps up the changes we need to make to the app's data layer.