Search code examples
ioscore-datadelaynsfetchedresultscontrolleruistoryboardsegue

NSFetchedResultsController causes delayed segue


I am building an application that looks similar to Apples Photos app on the iphone. Using a Master-Detail interface the user selects an album and gets taken to a UICollectionViewController that displays a bunch of photos. For both master and detail view controllers I'm using NSFetchedResultsControllers to access the objects from the database. Now unfortunately there is a very noticeable delay when an album is selected which is caused by the detail NSFetchedResultsController fetching the items.

Since apples photos app responds immediately and has all photos ready upon clicking I'm wondering how they did it.

Is there a best practice for this?

Code Example

//DetailViewController.swift

// lazy frc
var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest()
    // Edit the entity name as appropriate.
    let entity = NSEntityDescription.entityForName("Photo", inManagedObjectContext: self.context)
    fetchRequest.entity = entity

    // Set the batch size to a suitable number.
    fetchRequest.fetchBatchSize = 20

    // Edit the sort key as appropriate.
    let sortDescriptor = NSSortDescriptor(key: "photoID", ascending: false)
    let sortDescriptors = [sortDescriptor]

    fetchRequest.sortDescriptors = [sortDescriptor]

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.context, sectionNameKeyPath: nil, cacheName: "ernesto")
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    var error: NSError? = nil
    if !_fetchedResultsController!.performFetch(&error) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        //println("Unresolved error \(error), \(error.userInfo)")
        abort()
    }

    return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil


// MARK: - UICollectionViewDataSource
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! PhotoCell
    let item = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
    cell.backgroundColor = UIColor.yellowColor()
    //the following line is commented out intentionally to make sure the delay is not caused by setting the image
    //cell.imageView.image = UIImage(data: item.valueForKey("image") as! NSData)
    return cell
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
    return sectionInfo.numberOfObjects
}

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1
}

Solution

  • The problem is that you are using CoreData to store your pictures and apple does not. Though there is no limit on file size and on the total size your library can use it will take longer to get your files than opening a folder in the file structure. This will get worse as you add pictures to the app because the queries will return more and more data. This is in addition to the size of your app that will grow with each added photo. instead of CoreData you should store pics in the file system (and the path in CoreData) or in the library itself.

    If you go with the core data route, you can use several technics to accelerate things ranging from querying in batches to querying the count to build the collection view and only the images in the cells that are currently being shown.

    Another approach is to store each photo twice once in full resolution and one in a much smaller size. The smaller one is user to show the collection view, the animation between the collection and the detail and in the first few microseconds the photo is shown in the detail view before the full resolution picture gets there.