Refactoring the existing code

The existing code compiles, but it's not optimal yet. MovieDBHelper doesn't pass the movie's remote ID to its callback, and the movie-insertion code doesn't use this remote ID yet. When the user wants to save a new movie, the app still defaults to creating a new movie instead of using the handy new helper you just wrote to avoid data duplication. You should update the code, so the callback is called with the fetched remote ID.

Let's update MovieDBHelper first. Replace the following lines in the fetchRating(forMovie:callback:) method:

typealias MovieDBCallback = (Int?, Double?) -> Void
let apiKey = "d9103bb7a17c9edde4471a317d298d7e"

func fetchRating(forMovie movie: String, callback: @escaping MovieDBCallback) {
  guard let searchUrl = url(forMovie: movie) else {
    callback(nil, nil)
    return
  }

  let task = URLSession.shared.dataTask(with: searchUrl) { data, response, error in
    var rating: Double? = nil
    var remoteId: Int? = nil

    defer {
      callback(remoteId, rating)
    }

    let decoder = JSONDecoder()

    guard error == nil, let data = data,
      let lookupResponse = try? decoder.decode(MovieDBLookupResponse.self, from: data),
      let movie = lookupResponse.results.first
      else { return }

    rating = movie.popularity
    remoteId = movie.id
  }

  task.resume()
}

These updates change the callback-handler, so it takes both the remote ID and the rating as parameters. A variable to hold the remote ID is added and incorporated into the callback. With this code, MovieDBHelper is entirely up to date.

You should also update the response struct, so the MovieDBMovie struct includes the ID from the API response:

struct MovieDBMovie: Codable {  
  let popularity: Double?  
  let id: Int?  
}
moc.persist {
  // 1
  let movie = Movie.find(byName: name, orCreateIn: moc)

  // 2
  if movie.title == nil || movie.title?.isEmpty == true {
    movie.title = name
  }

  let newFavorites: Set <AnyHashable> = familyMember.movies?.adding(movie) ?? [movie]
  familyMember.movies = NSSet(set: newFavorites)

  let helper = MovieDBHelper()
  helper.fetchRating(forMovie: name) { remoteId, rating in
    guard let rating = rating,
      let remoteId = remoteId
      else { return }

    moc.persist {
      movie.popularity = rating
      movie.remoteId = Int64(remoteId)
    }
  }
}

First, the preceding code either fetches an existing movie or creates a new one with the find(byName:orCreateIn:) method you just created. Next, it checks whether the returned movie already has a name. If it doesn't have a name yet, the name is set. Also, if it does have a name, you can safely assume you were handed an existing movie object, so there is no need to set the name. Next, the rating and ID are fetched, and the corresponding properties on the movie object are set to their correct values in the callback.

This is all the code needed to prepare MustC for background fetch. Let's implement this feature now.