Skip to main content
Signature
SortByConnector(
  searcher: AnyObject & Searchable & IndexNameSettable,
  indicesNames: [IndexName],
  selected: Selected?,
  controller: SelectableSegmentController
)

About this widget

SortBy displays a list of indices, allowing a user to change the way hits are sorted (using replica indices). Another common use case is to let users switch between different indices to show different results. For this to work, you must define all indices that you pass to SortBy as replicas of the main index.

Examples

Instantiate a SortByConnector.
Swift
let searcher: HitsSearcher = .init(appID: "YourApplicationID",
                                          apiKey: "YourSearchOnlyAPIKey",
                                          indexName: "indexDefault")
let alertController = UIAlertController(title: "Change Index",
                                        message: "Please select a new index",
                                        preferredStyle: .actionSheet)
let selectIndexController: SelectIndexController = .init(alertController: alertController)
let sortByConnector: SortByConnector = .init(searcher: searcher,
                                             indicesNames: ["indexDefault",
                                                            "indexAscendingOrder",
                                                            "indexDescendingOrder"],
                                             selected: 0,
                                             controller: selectIndexController) { indexName -> String in
  switch indexName {
  case "indexDefault": return "Default"
  case "indexAscendingOrder": return "Year Asc"
  case "indexDescendingOrder": return "Year Desc"
  default: return indexName.rawValue
  }
}

Parameters

searcher
HitsSearcher
required
The Searcher that handles your searches.
interactor
QueryRuleCustomDataInteractor<Model>
required
default: .init()
The logic applied to the custom model.
controller
ItemController
default: nil
The Controller interfacing with a concrete custom data view.
presenter
Presenter<SearchStats?, Output>
default: nil
The Presenter defining how a model appears in the controller.

Presenter

IndexNamePresenter
IndexNamePresenter
default:"DefaultPresenter.IndexName.present"
The presenter that defines how to display an index, taking as input an indexName and returning a string.
Swift
public static let present: IndexNamePresenter = { indexName in
  return indexName.rawValue
}

Low-level API

If you want to fully control the SortBy components and connect them manually, you can use the following components:
  • Searcher. The Searcher that handles your searches.
  • SortByInteractor. The logic applied to the index sorting/switching.
  • SelectableSegmentController. The controller that interfaces with a concrete index list.
  • IndexPresenter. Optional. The presenter that converts an IndexName to a String output.
Swift
let searcher: HitsSearcher = .init(appID: "YourApplicationID",
                                          apiKey: "YourSearchOnlyAPIKey",
                                          indexName: "indexDefault")

let alertController = UIAlertController(title: "Change Index",
                                        message: "Please select a new index",
                                        preferredStyle: .actionSheet)
let selectIndexController: SelectIndexController = .init(alertController: alertController)

let indexSegmentInteractor: SortByInteractor = .init(items: [
    0 : "indexDefault",
    1 : "indexAscendingOrder",
    2 : "indexDescendingOrder"
])

indexSegmentInteractor.connectSearcher(searcher: searcher)

indexSegmentInteractor.connectController(selectIndexController) { indexName -> String in
  switch indexName {
  case "indexDefault": return "Default"
  case "indexAscendingOrder": return "Year Asc"
  case "indexDescendingOrder": return "Year Desc"
  default: return indexName.rawValue
  }
}

Customizing your view

The controllers provided by default, like the SelectIndexController work well when you want to use native UIKit with their default behavior like a UIAlertController. If you want to use another component, 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 SelectableSegmentController protocol.

Protocol

func setSelected(_ selected: Int?): Function called when an index is selected, with the position that is selected. func setItems(items: [Int: String]) Function called when a new array of indices is defined. func reload() Function called when a reload of the list view is required. var onClick: ((Int) -> Void)?: Closure to call when a new index is clicked.

Example

Swift
public class SelectIndexController: NSObject, SelectableSegmentController {

  let alertController: UIAlertController

  public var onClick: ((Int) -> Void)?

  public init(alertController: UIAlertController) {
    self.alertController = alertController
    super.init()
  }

  public func setSelected(_ selected: Int?) {
    // Show a check mark next to the item selected here
  }

  public func setItems(items: [Int: String]) {
    guard alertController.actions.isEmpty else { return }
    for item in items {
      alertController.addAction(UIAlertAction(title: item.value, style: .default, handler: { [weak self] _ in
        self?.onClick?(item.key)
      }))
    }
    alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: .none))
  }

}

SwiftUI

InstantSearch provides the SelectableSegmentObservableController data model, which is an implementation of the SelectableSegmentController protocol adapted for usage with SwiftUI. SelectableSegmentObservableController must be connected to the SortByConnector or SelectableSegmentInteractor like any other StatsTextController implementation. The example of the sort by view using the Menu component provided by SwiftUI.
Swift
struct ContentView: View {

  @ObservedObject var selectableSegmentObservableController: SelectableSegmentObservableController

  var body: some View {
    Menu {
      let segmentTitles = selectableSegmentObservableController.segmentsTitles
      ForEach(0..<segmentTitles.count) { segmentIndex in
        Button(segmentTitles[segmentIndex]) {
          selectableSegmentObservableController.select(segmentIndex)
        }
      }
    } label: {
      if let selectedSegmentIndex = selectableSegmentObservableController.selectedSegmentIndex {
        Label(selectableSegmentObservableController.segmentsTitles[selectedSegmentIndex], systemImage: "arrow.up.arrow.down.circle")
      }
    }
  }
}
If you prefer to create a custom index picker SwiftUI view, you can directly use the SelectableSegmentObservableController as a data model. It provides segmentsTitles and selectedSegmentIndex properties along with select function to streamline the design process of your custom SwiftUI view.
I