Multi Hits
About this widget
MultiHits
manages and displays a paginated list of search results from multiple indices. It performs multiple search requests simultaneously and enables some features of a good search experience like query suggestions.
To add multi-index hits to your search experience use these components:
MultiIndexSearcher
: TheSearcher
that handles your searches.MultiIndexHitsInteractor
: The logic applied to the multi-index search results.MultiIndexHitsController
: The controller that interfaces with a concrete multi-index hits view.
First of all, you have to instantiate a HitsInteractor
for each index. Then, instantiate the MultiIndexHitsInteractor
providing a list of the previously instantiated HitsInteractors as a parameter.
Please note that the position of an index in the list of indices used while instantiating MultiIndexSearcher
must match the position of HitsInteractor
in the list you use while instantiating the MultiIndexInteractor
parameter.
MultiIndexHitsController
is an optional parameter. It is useful if you want to show all the search results from different indices in one view.
Otherwise, you can keep references to HitsInteractors
of indices and create a separate HitsController
for each of them.
Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let searcher: MultiIndexSearcher = .init(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexNames: ["actors", "movies"])
let actorHitsInteractor: HitsInteractor<Actor> = .init(infiniteScrolling: .off)
let movieHitsInteractor: HitsInteractor<Movie> = .init(infiniteScrolling: .off)
lazy var hitsInteractor: MultiIndexHitsInteractor = { return .init(hitsInteractors: [actorHitsInteractor, movieHitsInteractor]) }()
let tableController: MultiIndexHitsTableController = .init(tableView: UITableView())
func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
hitsInteractor.connectSearcher(searcher)
hitsInteractor.connectController(tableController)
searcher.search()
}
Default controllers such as MultiIndexHitsTableController
and MultiIndexHitsCollectionController
provided by InstantSearch allow you to create a basic multi-index Hits
view based on the UITableView
and UICollectionView
components from UIKit.
These controllers can optionally be configured using the provided dataSource and delegate classes, which provide simplified, closure-based alternatives to UITableViewDataSource
and UITableViewDelegate
protocols.
For example, configuring the MultiIndexHitsTableController
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func configureTableController() {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
let dataSource = MultiIndexHitsTableViewDataSource()
dataSource.setCellConfigurator(forSection: 0) { (tableView, actor: Actor, indexPath) in
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
cell.textLabel?.text = actor.name
return cell
}
dataSource.setCellConfigurator(forSection: 1) { (tableView, movie: Movie, indexPath) in
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
cell.textLabel?.text = movie.title
return cell
}
tableController.dataSource = dataSource
let delegate = MultiIndexHitsTableViewDelegate()
delegate.setClickHandler(forSection: 0) { (tableView, actor: Actor, indexPath) in
// Action triggered when an actor is selected
}
delegate.setClickHandler(forSection: 1) { (tableView, movie: Movie, indexPath) in
// Action triggered when a movie is selected
}
tableController.delegate = delegate
}
Customization
MultiIndexHitsTableViewDataSource
and MultiIndexHitsTableViewDelegate
(as well as their UICollectionView
equivalents) can be subclassed to provide a customized behavior.
If you prefer having more control over your UITableView
or UICollectionView
, you can implement and assign your own dataSource and delegates.
MultiIndexHitsInteractor
provides all the necessary methods for building your own multi-sectional dataSource
such as:
numberOfSections() -> Int
: total number of indices (each index represents one section)numberOfHits(inSection section: Int) -> Int
: number of hits in the specified sectionhit<R: Codable>(atIndex index: Int, inSection section: Int) throws -> R?
hit object at the specified index for the specified section
Customize your view
The controllers provided by default, like the MultiIndexHitsTableController
or the MultiIndexHitsCollectionController
work well when you want to use native UIKit with their default behavior.
If you want to use another component as a multi-index hits view, or want to introduce some custom behavior to the already provided UIKit component, you can create your own controller conforming to the MultiIndexHitsController
protocol.
Protocol
var hitsSource: MultiIndexHitsSource?
:
Reference to an entity providing a list of hits per section.
func reload()
:
Function called when we require a reload of the hits view.
func scrollToTop()
:
Function called when we have to scroll to the top of the hits view.
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MultiIndexHitsTableController: NSObject, MultiIndexHitsController {
public let tableView: UITableView
public weak var hitsSource: MultiIndexHitsSource?
public init(tableView: UITableView) {
self.tableView = tableView
}
public func reload() {
tableView.reloadData()
}
public func scrollToTop() {
guard tableView.numberOfRows(inSection: 0) != 0 else { return }
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}