Reading data with a simple fetch request

The simplest way to fetch data from your database is to use a fetch request. The managed object context forwards fetch requests to the persistent store coordinator. The persistent store coordinator will then forward the request to the persistent store, which will then convert the request to a SQLite query. Once the results are fetched, they are passed back up this chain and converted to NSManagedObject instances.

By default, these objects are called faults. When an object is a fault, it means that the actual properties and values for the object are not fetched yet, but they will be fetched once you access them. This is an example of a good implementation of lazy variables because fetching the values is a pretty fast operation, and fetching everything up front would greatly increase your app's memory footprint because all values would have to be loaded into memory right away.

Let's take a look at an example of a simple fetch request that retrieves all FamilyMember instances that were saved to the database:

let request: NSFetchRequest<FamilyMember> = FamilyMember.fetchRequest()   

let moc = persistentContainer.viewContext

guard  let results = try? moc.fetch(request)   
  else { return } 

As you can see, it's not particularly hard to fetch all of your family members. Every NSManagedObject has a class method that configures a basic fetch request that can be used to retrieve data. If you have large amounts of data, you probably don't want to fetch all of the persisted objects at once. You can configure your fetch request to fetch data in batches by setting the fetchBatchSize property. It's recommended that you use this property whenever you want to use fetched data in a table view or collection view. You should set the fetchBatchSize property to a value that is just a bit higher than the number of cells you expect to display at a time. This makes sure that Core Data fetches plenty of items to display while avoiding loading everything at once.

Now that you know how to fetch data, let's display some data in the family member's table view. Add a new variable called familyMembers to FamilyMembersViewController. Give this property an initial value of [FamilyMember](), so you start off with an empty array of family members. Also, add the example fetch request you saw earlier to viewDidLoad(). Next, assign the result of the fetch request to familyMembers as follows:

familyMembers = results  

Finally, update the table view delegate methods so tableView(_:numberOfRowsInSection:) returns the number of items in the familyMembers array. Also, update the tableView(_:cellForRowAtIndexPath:) method by adding the following two lines before returning the cell:

let familyMember = familyMembers[indexPath.row]   
cell.textLabel?.text = familyMember.name 

If you build and run your app now, you should see the family members you already saved. New family members won't show up right away. However, when you quit the app and run it again, new members will show up.

You could manually reload the table view right after you insert a new family member so it's always up to date but this isn't the best approach. You will see a better way to react to the insertion of new data soon. Let's finish the family member detail view first so it shows a family member's favorite movies. Add the following code to the end of the prepare(for:sender:) method in the overview view controller:

if let moviesVC = segue.destination as? MoviesViewController {
  moviesVC.persistentContainer = persistentContainer
  moviesVC.familyMember = familyMembers[selectedIndex.row]
}

The preceding lines of code pass the selected family member and the persistent container to MoviesViewController so it can display and store the current family member's favorite movies.

All you need to do to show the correct movies for a family member is use the family member's favorite movies in the MovieViewController class's table-view data-source methods, as follows:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) ->Int {
  return familyMember?.movies?.count ?? 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  guard let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell"),
    let movies = familyMember?.movies
    else { fatalError("Wrong cell identifier requested or missing family member") }

  let moviesArray = Array(movies as! Set<Movie>)
  let movie = moviesArray[indexPath.row]
  cell.textLabel?.text = movie.title

  return cell
}

You don't need to use a fetch request here because you can simply traverse the movies relationship on the family member to get their favorite movies. This isn't just convenient for you as a developer, it's also good for your app's performance. Every time you use a fetch request, you force a query to the database. If you traverse a relationship, Core Data will attempt to fetch the object from memory instead of asking the database.

Again, adding new data won't immediately trigger the table view to update its contents. We'll get to that after we take a look at how to filter data. If you want to check whether your code works, build and rerun the app so all the latest data is fetched from the database.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset