Create your own InstantSearch iOS widgets
As of May 1st, 2024, Apple requires all iOS apps to include a privacy manifest. Ensure you incorporate our provided privacy manifest files into your documentation. For more details, see Privacy Manifest.
On this page
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.
- Implement it in a
- Create the connection methods between your
Interactor
and every other component:- Create a
connectController()
to connect yourInteractor
to itsController
. - If it uses the
Searcher
, aconnectSearcher()
. - If it uses the
FilterState
, aconnectFilterState()
.
- Create a
Example
You will build a widget that displays the number of searches made since it was last clicked.
Create the interactor
The Interactor
will be quite straightforward: it stores a sum
that can be increment
ed or reset
ed to 0. You will use InstantSearch’s Observer
to allow subscribing to changes of the sum
’s value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.
1
2
3
4
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
}
Implementing our Controller
We can now implement a SumSearchesButtonController
: it should display the data received in setSum
and trigger onReset
when clicked.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 our Interactor
and its Controller
(s), we’ll define a connection method to describe what should happen when we connect them (subscribe to sum
and set the reset callback).
You can do this in the Interactor
’s extension.
1
2
3
4
5
6
7
8
9
10
11
12
13
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
.
You’ll subscribe to the Searcher
’s onResults
, and call increment()
on every new search response.
1
2
3
4
5
6
7
8
9
extension SumSearchesInteractor {
func connectSearcher(_ searcher: HitsSearcher) {
searcher.onResults.subscribe(with: self) { (interactor, _) in
interactor.increment()
}
}
}
Final touches
You just created your first custom widget and can now use it in your application, like any other widget:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Initialize your Searcher as usual
let searcher = HitsSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
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)