UI libraries / InstantSearch iOS / Widgets
Signature
FacetFilterListConnector(
  facetFilters: [FacetFilter],
  selectionMode: SelectionMode,
  filterState: FilterState,
  operator: RefinementOperator,
  groupName: String,
  controller: FilterListTableController<Filter.Facet>
)

About this widget

FacetFiltersList is a filtering component that displays facet filters and lets users refine the search results by selecting them.

Compared to the RefinementList, which takes its values from the search response facets, this widget displays facet filters that you add yourself.

Examples

Instantiate a FacetFilterListConnector 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
let searcher = HitsSearcher(appID: "YourApplicationID",
                            apiKey: "YourSearchOnlyAPIKey",
                            indexName: "YourIndexName")
let filterState = FilterState()
let filters: [Filter.Facet] = ["red", "blue", "green", "yellow", "black"].map {
  .init(attribute: "color", stringValue: $0)
}
let filterListTableView: UITableView = .init()
let filterListController: FilterListTableController<Filter.Facet> = .init(tableView: filterListTableView)

let filterListConnector = FacetFilterListConnector(facetFilters: filters,
                                                   selectionMode: .multiple,
                                                   filterState: filterState,
                                                   operator: .and,
                                                   groupName: "Facet Filters",
                                                   controller: filterListController)

searcher.connectFilterState(filterState)
searcher.search()

Parameters

facetFilters
type: [FacetFilter]
default: []
Required

The facet filters to display.

selectionMode
type: SelectionMode
default: .multiple
Optional

Whether a user can select .single or .multiple values.

filterState
type: FilterState
Required

The FilterState that holds your filters.

operator
type: RefinementOperator
Required

Whether we apply an and or or behavior to the filters in the filterState.

For example if we have an or behavior, the filter sent to Algolia will be _tags:promo OR color:green.

groupName
type: String
Required

Filter group name.

controller
type: FilterListTableController<Filter.Facet>
default: nil
Optional

Controller interfacing with a concrete filter list view.

Low-level API

If you want to fully control the FacetFilterList components and connect them manually, you can use the following components:

  • Searcher: The Searcher that handles your searches.
  • FilterState: The current state of the filters.
  • FacetFilterListInteractor: The logic applied to the facet filters.
  • FilterListController: The view that will render the facet filters.
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()

let filters: [Filter.Facet] = ["red", "blue", "green", "yellow", "black"].map {
  .init(attribute: "color", stringValue: $0)
}

let filterListTableView: UITableView = .init()
let filterListController: FilterListTableController<Filter.Facet> = .init(tableView: filterListTableView)

let filterListInteractor = FacetFilterListInteractor(items: filters, selectionMode: .multiple)

filterListInteractor.connectFilterState(filterState, operator: .and, groupName: "Facet Filters")
filterListInteractor.connectController(filterListController)

searcher.connectFilterState(filterState)
searcher.search()

Customizing your view

The controllers provided by default, e.g., FilterListTableController, work well when you want to use native UIKit components 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 FacetFilterListController protocol.

Protocol

var onClick: ((Filter.Facet) -> Void)?:

Closure to call when a filter is clicked.

func setSelectableItems(selectableItems: [SelectableItem<Filter.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
class FacetFilterListTableController: NSObject, FacetFilterListController, UITableViewDataSource, UITableViewDelegate {

  public var onClick: ((Filter.Facet) -> Void)?

  public let tableView: UITableView

  public var selectableItems: [SelectableItem<Filter.Facet>] = []
  public var filterPresenter: FilterPresenter?

  let cellID: String

  public init(tableView: UITableView, cellID: String = "cellID") {
    self.tableView = tableView
    self.cellID = cellID
    super.init()
    tableView.dataSource = self
    tableView.delegate = self
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
  }

  // MARK: - FilterListController

  public func setSelectableItems(selectableItems: [SelectableItem<Filter.Facet>]) {
    self.selectableItems = selectableItems
  }

  public func reload() {
    tableView.reloadData()
  }

  // MARK: - UITableViewDataSource

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return selectableItems.count
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
    let filter = selectableItems[indexPath.row]
    let filterPresenter = self.filterPresenter ?? DefaultPresenter.Filter.present
    cell.textLabel?.text = filterPresenter(Filter(filter.item))
    cell.accessoryType = filter.isSelected ? .checkmark : .none

    return cell
  }

  // MARK: - UITableViewDelegate

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let selectableItem = selectableItems[indexPath.row]
    onClick?(selectableItem.item)
  }

}

SwiftUI

InstantSearch provides the FilterList SwiftUI view which you can embed in your views. It uses FilterListObservableController as a data model, which is an implementation of the SelectableListController protocol adapted for usage with SwiftUI. FilterListObservableController must be connected to the FacetFilterListConnector or FacetFilterListInteractor like any other SelectableListController implementation. You have to define the appearance of the view representing a single facet filter and its selection state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct ContentView: View {

  @ObservedObject var facetFiltersController: FilterListObservableController<Filter.Facet>

  var body: some View {
    FilterList(facetFiltersController) { filter, isSelected in
      // declare the view presenting a single facet filter and its selection state
      HStack {
        Text(filter.value.description)
        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 facet filters, you can directly use the FilterListObservableController<Filter.Facet> as a data model. It provides filters and selections properties along with toggle and isSelected functions to streamline the design process of your custom SwiftUI view.

Did you find this page helpful?