Refinement List
FacetListConnector( searcher: SingleIndexSearcher, filterState: FilterState, attribute: Attribute, selectionMode: SelectionMode, facets: [Facet], operator: RefinementOperator, groupName: String, controller: FacetListController, presenter: FacetListPresenter )
About this widget
RefinementList
is a filtering view that displays facets, and lets the user refine the search results by filtering on specific values.
Requirements
You must include the attribute provided to the widget in attributesForFaceting
, either using the dashboard or using attributesForFaceting
with the API.
Examples
Instantiate a FacetListConnector
and launch an initial search on its searcher.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let searcher = HitsSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexName: "YourIndexName")
let filterState: FilterState = .init()
let categoryTableViewController: UITableViewController = .init()
let categoryListController: FacetListTableController = .init(tableView: categoryTableViewController.tableView)
let facetListPresenter: FacetListPresenter = .init(sortBy: [.count(order: .descending)], limit: 5, showEmptyFacets: false)
let categoryConnector: FacetListConnector = .init(searcher: searcher,
filterState: filterState,
attribute: "category",
selectionMode: .multiple,
facets: [.init(value: "initial facet", count: 10)],
operator: .and,
controller: categoryListController,
presenter: facetListPresenter)
searcher.search()
Parameters
searcher
|
type: HitsSearcher
Required
The |
filterState
|
type: FilterState
Required
The |
attribute
|
type: Attribute
Required
The attribute to filter. |
selectionMode
|
type: SelectionMode
default: .multiple
Optional
Whether a user can select |
facets
|
type: [Facet]
default: []
If specified, the default facet value(s) to apply. |
operator
|
type: RefinementOperator
Required
Whether we apply an For example if we select |
groupName
|
type: String
default: The raw value of the `attribute` parameter
Optional
Filter group name. |
controller
|
type: FacetListController
default: nil
Optional
Controller interfacing with a concrete facet list view. |
presenter
|
type: SelectableListPresentable
default: nil
Optional
Presenter defining how a facet appears in the controller. |
Presenter
sortBy
|
type: [FacetSortCriterion]
default: [.count(order: .descending)]
Optional
How to sort facets. Must be one or more of the following values:
|
||
Copy
|
|||
limit
|
type: Int
default: 10
Optional
The number of facet values to retrieve. |
||
Copy
|
|||
showEmptyFacets
|
type: Bool
default: true
Optional
Whether we show facets that have a facet count of 0. |
||
Copy
|
Low-level API
If you want to fully control the RefinementList
components and connect them manually, you can use the following components:
Searcher
: TheSearcher
that handles your searches.FilterState
: The current state of the filters.FacetListInteractor
: The logic applied to the facets.FacetListController
: The controller that interfaces with a concrete facet list view.FacetListPresenter
: Optional. The presenter that controls the sorting and other settings of the facet list view.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let searcher = HitsSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexName: "YourIndexName")
let filterState: FilterState = .init()
let facetListInteractor: FacetListInteractor = .init(facets: [.init(value: "initial facet", count: 10)], selectionMode: .multiple)
let categoryTableViewController: UITableViewController = .init()
let categoryListController: FacetListTableController = .init(tableView: categoryTableViewController.tableView)
let facetListPresenter: FacetListPresenter = .init(sortBy: [.count(order: .descending)], limit: 5, showEmptyFacets: false)
facetListInteractor.connectSearcher(searcher, with: "category")
facetListInteractor.connectFilterState(filterState, with: "category", operator: .or)
facetListInteractor.connectController(categoryListController)
facetListInteractor.connectController(categoryListController, with: facetListPresenter)
searcher.connectFilterState(filterState)
searcher.search()
Customizing your view
The controllers provided by default, like the FacetListTableViewController
work well when you want to use native UIKit with their default behavior.
If you want to use another component such as a UICollectionView
, a third-party input view, or you want to introduce some custom behavior to the already provided UIKit component, you can create your own controller conforming to the FacetListController
protocol.
Protocol
var onClick: ((Facet) -> Void)?
:
Closure to call when a new facet is clicked.
func setSelectableItems(selectableItems: [SelectableItem<Facet>])
Function called when a new array of selectable facets is updated. This is the UI State of the refinement list.
func reload()
Function called when we require a reload of the list view.
Implementation example
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
open class FacetListTableViewController: NSObject, FacetListController {
public var onClick: ((Facet) -> Void)?
public var tableView: UITableView
var selectableItems: [SelectableItem<Facet>] = []
var cellID: String
public init(tableView: UITableView, cellID: String = "FacetList") {
self.tableView = tableView
self.cellID = cellID
super.init()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
tableView.dataSource = self
tableView.delegate = self
}
public func setSelectableItems(selectableItems: [SelectableItem<Facet>]) {
self.selectableItems = selectableItems
}
public func reload() {
tableView.reloadData()
}
}
extension FacetListTableViewController: UITableViewDataSource {
open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectableItems.count
}
open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let selectableRefinement: SelectableItem<Facet> = selectableItems[indexPath.row]
let facetAttributedString = NSMutableAttributedString(string: selectableRefinement.item.value)
let facetCountStringColor = [NSAttributedString.Key.foregroundColor: UIColor.gray, .font: UIFont.systemFont(ofSize: 14)]
let facetCountString = NSAttributedString(string: " (\(selectableRefinement.item.count))", attributes: facetCountStringColor)
facetAttributedString.append(facetCountString)
cell.textLabel?.attributedText = facetAttributedString
cell.accessoryType = selectableRefinement.isSelected ? .checkmark : .none
return cell
}
}
extension FacetListTableViewController: UITableViewDelegate {
open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectableItem = selectableItems[indexPath.row]
self.onClick?(selectableItem.item)
}
}
SwiftUI
InstantSearch provides the FacetList
SwiftUI view which you can embed in your views.
It uses FacetListObservableController
as a data model, which is an implementation of the FacetListController
protocol adapted for usage with SwiftUI.
FacetListObservableController
must be connected to the FacetListConnector
or FacetListInteractor, like any other
FacetListController implementation.
You can define the appearance of the view representing a single facet and its selection state or use the
FacetRow` view provided by InstantSearch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct ContentView: View {
@ObservedObject var facetListController: FacetListObservableController
var body: some View {
FacetList(facetListController) { facet, isSelected in
// Use the implementation provided by InstantSearch
// FacetRow(facet: facet,
isSelected: isSelected)
// Or declare a custom single facet view
HStack {
Text(facet.value)
Spacer()
if isSelected {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
.contentShape(Rectangle())
.frame(idealHeight: 44)
.padding(.horizontal, 5)
}
}
}
If you prefer to create a custom SwiftUI view that presents the list of facets, you can directly use the FacetListObservableController
as a data model.
It provides facets
and selections
properties along with convenient toggle
and isSelected
functions to streamline the design process of your custom SwiftUI view.
Check out the example to see this widget in action.