> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hits

> Shows a list of search results.

export const SearchQuery = () => <Tooltip tip="The text users enter into a search box. In the Search API, this corresponds to the query parameter. A search query is often used with filters, facets, and other parameters, but these aren't part of the query text itself.">
    search query
  </Tooltip>;

```swift Signature theme={"system"}
HitsConnector<Hit: Codable>(
  appID: ApplicationID,
  apiKey: APIKey,
  indexName: IndexName,
  infiniteScrolling: InfiniteScrolling,
  showItemsOnEmptyQuery: Bool,
  filterState: FilterState,
  controller: HitsController
)
```

<Card title="Explore example code" icon="github" href="https://github.com/algolia/instantsearch-ios/tree/master/Examples/Showcase">
  Browse the Hits example code on GitHub.
</Card>

## About this widget

Use the `Hits` component to display a list of search results.
The component automatically reloads results when new hits are fetched from Algolia.

`HitsConnector` is a generic class you can customize by implementing a class or structure that represents your record and conforms to the [`Codable`](https://developer.apple.com/documentation/swift/codable) protocol.
If, for some reason, the engine can't parse a record into the type you provided,
the `onError` event of the `Interactor` instance will be triggered.

If you prefer to deal with raw JSON objects, set `JSON` as the type of record and use the `rawHitAtIndex(_ row: Int) -> [String: Any]?` method to access a hit.

See also:

* [Multi-index search](/doc/guides/building-search-ui/ui-and-ux-patterns/multi-index-search/ios)
* [Searches without results](/doc/guides/building-search-ui/going-further/conditional-display/ios#handling-no-results)

## Examples

Define the custom type representing the hits in your index conforming to `Codable` protocol.

```swift Swift icon=code theme={"system"}
struct CustomHitModel: Codable {
  let name: String
}
```

Instantiate a `HitsConnector` and launch an initial search on its searcher.

```swift Swift icon=code theme={"system"}
let filterState: FilterState  = .init()
let hitsTableViewController = CustomHitsTableViewController()
let hitsConnector = HitsConnector<CustomHitModel>(appID: "YourApplicationID",
                                              apiKey: "YourSearchOnlyAPIKey",
                                              indexName: "YourIndexName",
                                              filterState: filterState,
                                              controller: hitsTableViewController)

hitsConnector.searcher.search()
```

## Parameters

<ParamField body="searcher" type="HitsSearcher | PlacesSearcher" required>
  The [`Searcher`](/doc/api-reference/widgets/instantsearch/ios) that handles your searches.
</ParamField>

<ParamField body="interactor" type="HitsInteractor" required>
  The logic applied to the hits.
</ParamField>

<ParamField body="filterState" type="FilterState">
  The [`FilterState`](/doc/api-reference/widgets/filter-state/ios) for your filters.
</ParamField>

<ParamField body="appID" type="ApplicationID" required>
  The ID of your application.
</ParamField>

<ParamField body="apiKey" type="APIKey" required>
  Your application's [search-only API key](/doc/guides/security/api-keys#search-only-api-key).
</ParamField>

<ParamField body="indexName" type="IndexName" required>
  Name of the index to search.
</ParamField>

<ParamField body="infiniteScrolling" type="InfiniteScrolling" post={["default: .on(withOffset: 5)"]}>
  Whether [infinite scrolling](/doc/guides/building-search-ui/ui-and-ux-patterns/infinite-scroll/ios) is enabled.
  `offset` defines the threshold for loading the next "page" of results.
  For example, if the index of the last visible hit is 10,
  and you set`offset` to 3, the engine will display the next page if the hit at index 13 (10+3) isn't loaded.
  If set to `.off`, this parameter turns off infinite scrolling.
</ParamField>

<ParamField body="showItemsOnEmptyQuery" type="Bool" default={true}>
  If `false`, no results are displayed when users haven't entered any <SearchQuery /> text.
</ParamField>

<ParamField body="controller" type="HitsController" post={["default: nil"]}>
  The controller interfacing with a concrete hits view.
</ParamField>

## Low-level API

To fully control the `Hits` components and connect them manually, use the following components:

* [`Searcher`](/doc/api-reference/widgets/instantsearch/ios). Handles your searches.
* `HitsInteractor`. The logic applied to the hits.
* `HitsController`. The controller that interfaces with a concrete hits view.
* `FilterState`. The current state of the filters.

```swift Swift icon=code theme={"system"}
let searcher = HitsSearcher(appID: "YourApplicationID",
                            apiKey: "YourSearchOnlyAPIKey",
                            indexName: "YourIndexName")

let filterState: FilterState  = .init()
let hitsInteractor: HitsInteractor<CustomHitModel> = .init()
let hitsTableViewController = CustomHitsTableViewController()

hitsInteractor.connectSearcher(searcher)
hitsInteractor.connectFilterState(filterState)
hitsInteractor.connectController(hitsTableViewController)
searcher.search()
```

Now, each time you launch a new search:

* The `Searcher` receives new results and transmits them to `HitsInteractor`
* The `HitsInteractor` parses search results and notifies `HitsController`
* The `HitsController` refreshes the view presenting the hits

## HitsController

The default controllers, `HitsTableViewController` and `HitsCollectionViewController`,
let you create a basic `Hits` view based on `UITableView` and `UICollectionView` components from UIKit.

You must configure these controllers using the `TableViewCellConfigurable`/`CollectionViewCellConfigurable` implementation protocols that define how your hit model is bound to a concrete cell class.
Here is an example of the implementation using the `UITableViewCell` and `CustomHitModel`.

```swift Swift icon=code theme={"system"}
struct CustomCellConfigurator: TableViewCellConfigurable {
  let model: CustomHitModel

  init(model: CustomHitModel, indexPath: IndexPath) {
    self.model = model
  }

  func configure(_ cell: UITableViewCell) {
    cell.textLabel?.text = model.name
  }
}
```

Define a convenient type alias for your view controller using the `CellConfigurable` implementation.

```swift Swift icon=code theme={"system"}
typealias CustomHitsTableViewController = HitsTableViewController<CustomCellConfigurator>
```

## Customization

You can subclass the `HitsTableViewController` or `HitsCollectionViewController`to customize their behavior.

```swift Swift icon=code theme={"system"}
class MoreCustomHitsTableViewController: HitsTableViewController<CustomCellConfigurator> {
  ...
}
```

## Customizing your view

The default controllers, `HitsTableViewController` and `HitsCollectionViewController`,
work well when using native UIKit components with default behaviors.

If you want to use another component as a hits view or introduce some custom behavior to the already provided UIKit component,
you can create a controller conforming to the `HitsController` protocol.

### Protocol

`var hitsSource: DataSource?`:

Reference to an entity providing a list of hits.

`func reload()`:

Function called when a reload of the hits view is required.

`func scrollToTop()`:

Function called when scrolling to the top of the hits view.

### Implementation example

```swift Swift icon=code theme={"system"}
public class CustomHitsTableViewController: NSObject, HitsController {

  public let tableView: UITableView

  public weak var hitsSource: HitsInteractor<Item>?

  public init(tableView: UITableView) {
    self.tableView = tableView
  }

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

  public func scrollToTop() {
    guard tableView.numberOfRows(inSection: 0) != 0 else { return }
    let indexPath = IndexPath(row: 0, section: 0)
    self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
  }

}
```

### SwiftUI

InstantSearch provides the `HitsList` SwiftUI view, which you can embed in your views.
It uses `HitsObservableController` as a data model.
`HitsObservableController` is an implementation of the `HitsController` protocol adapted for usage with SwiftUI.
`HitsObservableController` must be connected to the `HitsConnector` or `HitsInteractor` like any other `HitsController` implementation.
You should define the view representing a single hit.

```swift Swift icon=code theme={"system"}
struct HitItem: Codable {
  let name: String
}

struct ContentView: View {

  @ObservedObject var hitsController: HitsObservableController<HitItem>

  var body: some View {
    HitsList(hitsController) { (hit, _) in
      // declare the view representing a single hit
      VStack(alignment: .leading, spacing: 10) {
        Text(hit?.name ?? "")
        Divider()
      }
    } noResults: {
      Text("No Results")
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
  }
}
```

If you prefer to create a custom SwiftUI view that presents the list hits, use `HitsObservableController` as a data model.
It provides the `hits` property to streamline the design process of your custom SwiftUI view.
The `notifyAppearanceOfHit(atIndex)` function of `HitsObservableController` might be called on the appearance of each hit to ensure the correct operation of the infinite scrolling feature.

## Result metadata and Hit structure

Each hit returned by Algolia is enriched with search metadata,
like [`highlightResult`, `objectID`, and `snippetResult`](/doc/rest-api/search/search-single-index#response-hits).
InstantSearch provides the `Hit` wrapper structure to parse these.
This generic structure encapsulates your record type and gives strongly typed access to a hit's metadata.

For example, consider the following record structure:

```swift Swift icon=code theme={"system"}
struct Movie: Codable {
  let title: String
  let year: Int
}

/* An appropriate structure for representing a record in the following JSON format:

{
  "title": "Titanic",
  "year": 1997
}
*/
```

Conforming to the `Codable` protocol, the record is ready to use with the `HitsInteractor` as follows:

```swift Swift icon=code theme={"system"}
let hitsInteractor = HitsInteractor<Movie>()
```

However, by doing this, the engine will ignore all the hit metadata.
To keep this metadata, wrap your record structure into the provided `Hit` structure.

```swift Swift icon=code theme={"system"}
let hitsInteractor = HitsInteractor<Hit<Movie>>()
```

You can extract your `Movie` object by accessing the `object` field of `Hit`:

```swift Swift icon=code theme={"system"}
let movieHit: Hit<Movie> = hitsInteractor.hit(atIndex: ...)
let movie: Movie = movieHit.object
```

The `Hit` structure gives access to the following fields:

```swift Swift icon=code theme={"system"}
// Hit identifier attributed by Algolia
let objectID: String

// Wrapped record object
let object: T

// Snippeted attributes.
let snippetResult: TreeModel<SnippetResult>?

// Highlighted attributes. Each attribute contains an object or an array of objects (if the attribute in question is an array) with the following attributes.
let highlightResult: TreeModel<HighlightResult>?

// Ranking information.
let rankingInfo: RankingInfo?

// Geolocation information
let geolocation: Point?
```
