IQListKit
Model driven UITableView/UICollectionView.
IQListKit allows us to use UITableView/UICollectionView without implementing the dataSource. Just provide the section and their models with cell type and it will take care of rest including the animations of all changes.
For iOS13: Thanks to Apple for NSDiffableDataSourceSnapshot
For iOS12 and below: Thanks to Ryo Aoyama for DiffableDataSources
Requirements
Library | Language | Minimum iOS Target | Minimum Xcode Version |
---|---|---|---|
IQListKit (1.0.0) | Swift | iOS 13.0 | Xcode 11 |
IQListKit (1.1.0) | Swift | iOS 9.0 | Xcode 11 |
Swift versions support
5.0 and above
Installation
Installation with CocoaPods
IQListKit is available through CocoaPods. To install
it, simply add the following line to your Podfile:
Installation with Source Code
Drag and drop IQListKit
directory from demo project to your project
Installation with Swift Package Manager
Swift Package Manager(SPM) is Apple's dependency manager tool. It is now supported in Xcode 11. So it can be used in all appleOS types of projects. It can be used alongside other tools like CocoaPods and Carthage as well.
To install IQListKit package into your packages, add a reference to IQListKit and a targeting release version in the dependencies section in Package.swift
file:
To install IQListKit package via Xcode
- Go to File -> Swift Packages -> Add Package Dependency...
- Then search for https://github.com/hackiftekhar/IQListKit.git
- And choose the version you would like
How to use IQListKit?
If you wish to learn using a presentation then download the presentation PDF here: Presentation PDF
We'll be learning IQListKit using a simple example.
Let's say we have to show a list of users in a UITableView and for that, we have User Model like this:
Before going deep into the implementation, we have to learn about the Hashable protocol.
Now what is Hashable? I never used it before.
A Hashable protocol is used to determine the uniqueness of the object/variable. Technically a hashable is a type that has hashValue in the form of an integer that can be compared across different types.
Many types in the standard library conform to Hashable: String, Int, Float, Double and Bool values and even Set are hashable by default.
To confirm the Hashable protocol, we have to modify our model a little bit like below:
But if we would like to manually confirm, we have to implement func hash(into hasher: inout Hasher) and preferably we should also confirm to the Equatable protocol by implementing static func == (lhs: User, rhs: User) -> Bool like below:
Now let's come back to the implementation part. To use the IQListKit, we have to follow a couple of steps:
Step 1) Confirm our "UserCell" to "IQModelableCell" protocol
What is IQModelableCell protocol? and how we should confirm it?
The IQModelableCell protocol says that, whoever adopts me, have to expose a variable named model and it can be any type conforming to the Hashable.
Let's say we have UserCell like this:
There are a couple of ways we could easily confirm it by exposing a model named variable.
Method 1: Directly using our User model
Method 2: typealias our User model to common name like 'Model'
Method 3: By creating a Hashable struct in each cell (Preferred)
This method is preferable because it will have the ability to use multiple parameters in the model
Step 2) Connect the model with the cell
To do this, we could easily do it by implementing the didSet of our model variable
Step 3) Creating and configuring the IQList variable
Let's say we have a UsersTableViewController like this:-
Now we'll be creating an instance of IQList and providing it the list of models and cell type.
The listView parameter accepts either a UITableView or UICollectionView.
The delegateDataSource parameter is optional, but preferable when we would like
to do additional configuration in our cell before display
or to get callbacks when the cell is clicked.
Step 4) Provide the models with cell types to the IQList in the performUpdates method
Let's do this in a separate function called refreshUI
Now whenever our users array changes, we will be calling the refreshUI() method to reload tableView and that's it.
?
UITableView/UICollectionView delegate and datasource replacements
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
The IQListKit is a model-driven framework, so we'll be dealing with the Cell and models instead of the IndexPath. The IQListKit provides a couple of delegates to modify the cell or do additional configuration based on their model before the cell display. To do this, we can implement a delegate method of IQList like below:-
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Ahh, Don't worry about that. We'll provide you the user model associated with the cell directly. It's interesting!
- func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
Because this method mostly return values based on cell and it's model, we have moved these configurations to cell. This is part of the IQCellSizeProvider protocol and we can override the default behaviour.
- func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
- func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
- func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
Well, this method also mostly return values based on the cell and it's model, we have moved these configurations to cell. This is part of the IQCellActionsProvider protocol and we can override the default behaviour.
- func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
- func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)
This method also mostly return values based on the cell and it's model, we have moved these configurations to cell. This is also part of the IQCellActionsProvider protocol and we can override the default behaviour.
Other useful delegate methods
Other useful data source methods
Other useful IQModelableCell properties
Other useful IQModelableCell methods
Workarounds
IQListKit! ? Why are you not loading my cell created in storyboard?
Well. If we are creating cell in storyboard, then to work with the IQListKit we must have to put the cell identifier exactly same as it's class name. If we are using The UICollectionView then we also have to manually register our cell using list.registerCell(type: UserCell.self, registerType: .storyboard) method because with the UICollectionView, there is no way to detect if a cell is created in storyboard.
I have a large data set and list.performUpdates
method takes time to animate the changes ?. What can I do?
You would not believe the performUpdtes method is Background Thread Safe ?. We can call it in background and can show a loading indicator. In the completion handler we can hide the loading indicator. Under the hood, the change calculations will be done in background. Thanks again to Apple for NSDiffableDataSourceSnapshot and Ryo Aoyama for DiffableDataSources. The UITableView/UICollectionView will be reloaded in main thread. Please refer the below code:-