Hits
About this widget
The Hits
components display a list of search results. They are reloaded automatically when new hits are fetched from Algolia.
To add Hits
to your search experience, use these components:
Searcher
: TheSearcher
that handles your searches.HitsInteractor
: The logic applied to the hits.HitsController
: The controller that interfaces with a concrete hits view.
HitsInteractor
has a generic Record
parameter that represents a record of your index and must conform to the Codable protocol.
If, for some reason, your record can’t be parsed into the type you provided, the onError
event of the Interactor
instance will be triggered.
In case you prefer to deal with raw JSON objects, set JSON
as the type of record and use the rawHitAtIndex(_ row: Int) -> [String: Any]?
method to access a hit.
Examples
Connect HitsInteractor
and HitsController
with each other using the provided connection method.
Connect HitsInteractor
with Searcher
.
In this example, we use the HitsTableController
provided by InstantSearch.
Now, each time you launch a new search:
Searcher
receives new results and transmit them toHitsInteractor
HitsInteractor
parses search results and notifiesHitsController
HitsController
refreshes the view presenting the hits
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let searcher: SingleIndexSearcher = SingleIndexSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexName: "YourIndexName")
let hitsInteractor: HitsInteractor<JSON> = .init()
let hitsTableController: HitsTableController<HitsInteractor<JSON>> = .init(tableView: UITableView())
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
hitsInteractor.connectSearcher(searcher)
hitsInteractor.connectController(hitsTableController)
searcher.search()
}
Parameters
infiniteScrolling
|
type: InfiniteScrolling
default: .on(withOffset: 5)
Optional
Infinite scrolling setting. Offset defines the threshold of loading next page.
For example, if the index of the last visible hit is 10, and offset is set to 3, the next page will be loaded if the hit at index 13 (10+3) is not loaded.
If set to |
||
Copy
|
|||
showItemsOnEmptyQuery
|
type: Bool
default: true
Optional
If |
||
Copy
|
HitsController
The controllers provided by default, like the HitsTableController
or the HitsCollectionController
, allow you to create a basic Hits
view based on UITableView
and UICollectionView
components from UIKit.
These controllers can optionally be configured using HitsTableViewDataSource
and HitsTableViewDelegate
instances which are simplified, closure-based alternatives to the UITableViewDataSource
and UITableViewDelegate
protocols.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func configureTableController() {
hitsInteractor.connectSearcher(searcher)
hitsInteractor.connectController(hitsTableController)
hitsTableController.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
hitsTableController.dataSource = .init(cellConfigurator: { tableView, hit, indexPath in
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
cell.textLabel?.text = [String: Any](hit)?["title"] as? String
return cell
})
hitsTableController.delegate = .init(clickHandler: { tableView, hit, indexPath in
// action when a hit is selected
})
}
Customization
HitsTableViewDataSource
and HitsTableViewDelegate
(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.
HitsInteractor
provides all the necessary methods to build your own dataSource
:
-
numberOfHits() -> Int
:
number of hits stored in HitsInteractor -
hit(atIndex index: Int) -> Record?
:
an object representing a hit for specified index
Customize your view
The controllers provided by default, like the HitsTableController
or the HitsCollectionController
work well when you want to use native UIKit with their default behavior.
If you want to use another component as a hits view, or want to introduce some custom behavior to the already provided UIKit component, you can create your own controller conforming to the HitsController
protocol.
Protocol
var hitsSource: DataSource?
:
Reference to an entity providing a list of hits.
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
public class HitsTableController<Source: HitsSource>: NSObject, InstantSearchCore.HitsController {
public let tableView: UITableView
public weak var hitsSource: Source?
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)
}
}
Result metadata and Hit structure
Each hit returned by Algolia is enriched with search metadata, like highlightResult
, objectID
, and snippetResult
.
InstantSearch provides an easy way to parse these using the Hit
wrapper structure. This is a generic structure that encapsulates the type of your record, and gives a strongly typed access to metadata of a hit.
For example, consider the following record structure:
1
2
3
4
5
6
7
8
9
10
11
12
struct Movie: Codable {
let title: String
let year: Int
}
/* An appropriate structure for representing a record in the following json format:
{
"title": "Titanic",
"year": 1997
}
*/
Conforming to the Codable
protocol, it is ready to use with the HitsInteractor
as follows:
1
let hitsInteractor = HitsInteractor<Movie>()
However, by doing this, all the metadata which comes with each hit will be ignored.
To keep search metadata, wrap your record structure into the provided Hit
structure.
1
let hitsInteractor = HitsInteractor<Hit<Movie>>()
You can still extract your Movie
object by accessing the object
field of Hit
:
1
2
let movieHit: Hit<Movie> = hitsInteractor.hit(atIndex: ...)
let movie: Movie = movieHit.object
The Hit
structure gives access to the following fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Hit identifier attributed by Algolia
let objectID: String
// Wrapped record object
let object: T
// Snippeted attributes.
let snippetResult: [String: SnippetResult]?
// Highlighted attributes. Each attribute contains an object or an array of objects (if the attribute in question is an array) with the following attributes.
let highlightResult: [String: [HighlightResult]]?
// Ranking information.
let rankingInfo: RankingInfo?