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 forward 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 NSManagedObjects. By default, these objects are 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 begin accessing them. This can greatly reduce your app's memory usage.

Let's take a look at an example of a simple fetch request:

let request: NSFetchRequest<FamilyMember> = FamilyMember.fetchRequest() 
 
guard let moc = managedObjectContext, 
    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 pieces of data 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 use the fetched data in a table view, for example. You should set this property to a value that is just a bit higher than the amount of cells you expect to display at a time.

By default, objects are fetched as faults. This behavior is usually the behavior you would want because it will help you with memory management. Prior to iOS 10, faulting could cause some issues if you loaded a faulted object that got deleted from the persistent store before you accessed any of its properties. As you might expect, this would cause errors because the framework would attempt to load the properties you're accessing, except they don't exist anymore.

In iOS 10, Core Data makes use of snapshots; so, whenever you have a set of results, you can always safely access them even if they're faults. Once you persist or refresh the managed object context, any changes will be merged and you get a new snapshot that you can use from that moment on. This is really convenient, and it makes your data safer to use.

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

familyMembers = results  

Finally, update the table view delegate methods so tableView(_:numberOfRowsInSection:) returns the amount 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. You could manually reload the table view right after you insert a new family member, but this isn't the best approach. We'll get to a better way soon. Let's finish the family member detail view first so we can see some favorite movies and add new ones. Add the following code to the prepare(for:sender:) method in the overview view controller:

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

The preceding lines of code will pass the selected family member and the managed object context on to the MoviesViewController so it can display and store our family member's favorite movies.

All we need to do to make this work now is to use the family member's favorite movies in the MovieViewController class' table view data source methods, as follows:

func tableView(_ tableView: UITableView, numberOfRowsInSection 
  section: Int) ->Int { 
    return familyMember?.favoriteMovies?.count ?? 0 
} 
 
func tableView(_ tableView: UITableView, cellForRowAtindexPath: 
  IndexPath) ->UITableViewCell { 
    guard let cell = tableView.dequeueReusableCell
      (withIdentifier: "MovieCell"), 
        let movies = familyMember?.favoriteMovies 
        else { fatalError("Wrong cell identifier requested") } 
 
    let moviesArray = Array(movies as! Set<Movie>) 
    let movie = moviesArray[indexPath.row] 
    cell.textLabel?.text = movie.name 
 
    return cell 
} 

We don't need to use a fetch request here because we can simply traverse the favoriteMovies 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 issue 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.

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

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