API Reference / InstantSearch iOS Widgets / Multi Hits
Apr. 24, 2019

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: The Searcher 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 section
  • hit<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 components 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)
  }

}

Did you find this page helpful?