Skip to main content
Since May 1st, 2024, Apple requires all iOS apps to include a privacy manifest. For more details, see Privacy Manifest.
If none of the existing widgets fit your use-case, you could implement your own widget.
You are trying to create your own widget with InstantSearch iOS and that’s awesome but that also means that you couldn’t find the widgets or built-in options you were looking for. Algolia would love to hear about your use case as the aim with the InstantSearch libraries is to provide the best out-of-the-box experience. Don’t hesitate to send a quick message explaining what you were trying to achieve either using the form at the end of that page or directly by submitting a feature request.
Creating a widget takes three steps:
  • Create the MyWidgetInteractor, containing the business logic for your widget.
  • Create a MyWidgetController interface, describing the rendering of the widget data.
    • Implement it in a MyConcreteWidgetController that you will use.
  • Create the connection methods between your Interactor and every other component:
    • Create a connectController() to connect your Interactor to its Controller.
    • If it uses the Searcher, a connectSearcher().
    • If it uses the FilterState, a connectFilterState().

Example

You will build a widget that displays the number of searches made since it was last clicked.

Create the interactor

The Interactor stores a sum that can be incremented or reset to 0. You can use InstantSearch’s Observer to allow subscribing to changes of the sum’s value.
Swift
class SumSearchesInteractor {

  var sum: Int = 0 {
    didSet {
      onSumChanged.fire(sum)
    }
  }

  public let onSumChanged: Observer<Int> = .init()

  func increment() {
    sum += 1
  }

  func reset() {
    sum = 0
  }

}

Create the controller interface

To interact with the data in the ViewModel, you need a view than can display a number, and handle clicks to reset the counter.
Swift
protocol SumSearchesController {
    func setSum(sum: Int) // will be called on new sum
    var onReset: (() -> Void)? { get set } // will hold the callback to reset the sum
}

Implement the SumSearchesButtonController

The SumSearchesButtonController should display the data received in setSum and trigger onReset when clicked.
Swift
class SumSearchesButtonController : SumSearchesController {

  let button: UIButton
  var onReset: (() -> Void)?

  init(button: UIButton) {
    self.button = button
    button.addTarget(self, action: #selector(didPressButton), for: .touchUpInside)
  }

  func setSum(sum: Int) {
    button.setTitle("\(sum)", for: .normal)
  }

  @objc func didPressButton() {
    onReset?()
  }

}

Create the connectController method

To link the Interactor with its Controller, define a connection method to describe what should happen when connecting them (subscribe to sum and set the reset callback). You can do this in the Interactor extension.
Swift
extension SumSearchesInteractor {

  func connectController<Controller: SumSearchesController>(_ controller: Controller) {
    onSumChanged.subscribePast(with: self) { (interactor, sum) in
      controller.setSum(sum: sum)
    }

    controller.onReset = { [weak self] in
      self?.reset()
    }
  }

}

Create the connectSearcher method

Because the widget needs to be aware of searches to count them, it needs to be connected to a Searcher. Subscribe to the searcher’s onResults, and call increment for every new search response.
Swift
extension SumSearchesInteractor {

  func connectSearcher(_ searcher: HitsSearcher) {
    searcher.onResults.subscribe(with: self) { (interactor, _) in
      interactor.increment()
    }
  }

}

Final touches

You can now use your custom widget in your application, like any other widget:
Swift
// Initialize your Searcher as usual
let searcher = HitsSearcher(appID: "ALGOLIA_APPLICATION_ID",
                        apiKey: "ALGOLIA_SEARCH_API_KEY",
                        indexName: "YourIndexName")

// Create your Interactor and Controller implementation
let sumSearchesInteractor = SumSearchesInteractor()
let sumSearchesButton = UIButton()
let sumSearchesButtonController = SumSearchesButtonController(button: sumSearchesButton)

// Connect your Interactor to start displaying the count of searches
sumSearchesInteractor.connectSearcher(searcher)
sumSearchesInteractor.connectController(sumSearchesButtonController)
I