IGListKit/Guides/Best Practices and FAQ.md
Tim Oliver 2ed59fcf80 Update 'master' branch references to 'main' branch
Summary:
G'day folks! As promised, I'm spending a bit of my free time seeing what I can do to update and refresh the IGListKit repo on GitHub.

I noticed last night some strange behaviour in that the GitHub Actions CI wasn't running when new commits were merged into the main branch. I discovered the cause of this was because the `CI.yml` file still had `master` in its build rules instead of `main`. And once I noticed that, I noticed there were a lot of other references to the main branch being called `master` in a lot of the documentation.

Thankfully within the documentation, GitHub was smart enough to automatically redirect all of the `master` URLs to `main`, so nothing visibly broke, but I definitely think we should update all of that. :)

I went through the entire repo and did a thorough audit in all of its text files, and updated the main branch name accordingly.

Reviewed By: DimaVartanian

Differential Revision: D42990133

fbshipit-source-id: d6b06c40b1b959990856b46b048895e3c55a9870
2023-02-08 18:34:25 -08:00

8.6 KiB

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
  }
}