Best Practices and FAQs

This guide provides notes and details on best practices in using IGListKit, general tips, and answers to FAQs.

Best Practices

  • We recommend adding an assert to check -isKindOfClass: on the object you receive in -didUpdateToObject: in your section controllers. This makes it easy to track down easily-overlooked mistakes in your IGListAdapaterDataSource implementation. If this assert is ever hit, that means IGListKit has sent your section controller the incorrect type of object. This would only happen if your objects provide non-unique diff identifiers.
// Objective-C
- (void)didUpdateToObject:(id)object {
    NSParameterAssert([object isKindOfClass:[MyModelClass class]]);
    _myModel = object;
}
// Swift
func didUpdate(to object: Any) {
    precondition(object is MyModelClass)
    myModel = object as! MyModelClass
}
  • Make sure your -diffIdentifier implementation returns a unique identifier for each object.

  • We highly recommend using single-item sections when possible. That is, each section controller manages a single model (which may have one or multiple cells). This gives you the greatest amount of flexibility, modularity, and re-use for your components.

Frequently asked questions

I upgraded IGListKit and now everything is broken!

Check out our migration guide to make upgrading easier.

How do you implement separators between cells?

See discussion in #329

How do I fix the error Could not build Objective-C module 'IGListKit'?

See discussion in #316

The documentation and examples have <X> feature or changes, but I don’t have it in my version. Why?

This feature is on the main branch only and hasn’t been officially tagged and released. If you need to, you can install from the main branch.

Does IGListKit work with…?

Yes.

Does IGListKit work with UITableView?

No, but you can install the diffing subspec via CocoaPods.

What’s the purpose of IGListCollectionView?

Historically, we used this subclass to gain compile-time safety to prevent disallowed methods from being called on UICollectionView, because IGListKit handles model and view updates. However, it has since been removed. See discussion at #409.

How can I manage cell selection and deselection?

See discussion at #184.

I have a huge data set and -performUpdatesAnimated: completion: is super slow. What do I do?

If you have multiple thousands of items and you cannot batch them in, you’ll see performance issues with -performUpdatesAnimated: completion:. The real bottleneck behind the scenes here is UICollectionView attempting to insert so many cells at once. Instead, call -reloadDataWithCompletion: when you first load data. Behind the scenes, this method does not do any diffing and simply calls -reloadData on UICollectionView. For subsequent updates, you can then use -performUpdatesAnimated: completion:.

How do I use IGListKit and estimated cell sizes with Auto Layout?

This should work in theory, and we have an example section controller, but the estimated-size API in UICollectionViewFlowLayout has changed dramatically over different iOS versions, making first-class support in IGListKit difficult. We don’t use estimated cell sizes or Auto Layout in Instagram and cannot commit to fully supporting it.

See #516 for a master list of all known issues. We very much welcome contribution to fixing this!

Is creating a “wrapper” model just for IGListKit ok?

Yes! We create models that act as a grab-bag for other models, specifically for use in section controllers. Things like:

class WeatherSectionModel {
  let location: Location
  let forecast: Forecast
  let conditions: Conditions
}

Just don’t forget to make your models diffable using the data in the contained models:

extension WeatherSectionModel: ListDiffable {
  func diffIdentifier() -> NSObjectProtocol {
    return location.identifier
  }

  func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
    guard self !== object else { return true }
    guard let object = object as? WeatherSectionModel else { return false }
    return location == object.location && forecast == object.forecast && conditions == object.conditions
  }
}

What if I want to make my Swift structs diffable?

Give this box a try.

I want to deliver messages to certain section controllers, how do I do that?

We recommend using dependency injection and announcing changes, demonstrated in our example.

Should I reuse my section controllers between models?

No! IGListKit is designed to have a 1:1 instance mapping between objects and section controllers. IGListKit does not reuse section controllers, and if you do unintended behaviors will occur.

IGListKit does still use UICollectionView‘s cell reuse, so you shouldn’t be concerned about performance.

Why does UICollectionViewFlowLayout put everything in a new row?

UICollectionViewFlowLayout has its limitations, and it’s not well designed to support sections on the same “line”. Instead you should use IGListCollectionViewLayout.

What if I just want a section controller and don’t need the object?

Feel free to use a static string or number as your model. You can use this object as a “key” to find your section controller. Take a look at our example of this.

How do I make my cells diff and animate?

Use IGListBindingSectionController to automatically diff and animate your cells.

How can I power and update the number of items in a section controller with a dynamic array?

We recommend creating a model that owns an array to the items that power numberOfItems. Checkout our Post example that has dynamic comment cells. Just be sure to check when your array changes:

class Forecast: ListDiffable {
  let day: Date
  let hourly: [HourlyForecast]

  func diffIdentifier() -> NSObjectProtocol {
    return day
  }

  func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
    guard self !== object else { return true }
    guard let object = object as? Forecast else { return false }
    return hourly == object.hourly // compare elements in the arrays
  }
}