Create Your Own Widgets
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. We’d love to hear about your use case as our mission with our InstantSearch libraries is to provide the best out-of-the-box experience. Don’t hesitate to send us 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.
Overview
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
We will build a widget that displays the number of searches made since it was last clicked.
Create the Interactor
Our Interactor
will be quite straightforward: it stores a sum
that can be increment
ed or reset
ed to 0.
We 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 our ViewModel
, we 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).
We 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 our widget needs to be aware of searches to count them, it needs to be connected to a Searcher
.
We’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: SingleIndexSearcher) {
searcher.onResults.subscribe(with: self) { (interactor, _) in
interactor.increment()
}
}
}
Putting it all together
You just created your first custom widget, congratulations! 🎉
Now you can use your widget 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 = SingleIndexSearcher(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)