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

# Implement backend search with InstantSearch on iOS

> Implement server-side search in your backend on iOS.

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>;

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

export const Facet = () => <Tooltip tip="An attribute in your records that lets users filter or group results (for example, by color, brand, or price)." cta="Faceting" href="/doc/guides/managing-results/refine-results/faceting">
    facet
  </Tooltip>;

<div className="not-prose algolia-flavor-switcher">
  <div className="afs-dropdown">
    <div className="afs-trigger" role="button" tabIndex="0" aria-haspopup="listbox">
      <span className="afs-current">iOS</span>

      <svg className="afs-chevron lucide lucide-chevron-down" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <path d="m6 9 6 6 6-6" />
      </svg>
    </div>

    <ul className="afs-menu" role="listbox">
      <li role="option" aria-selected="false"><a className="afs-option" href="/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/js"><span className="afs-option-name">JavaScript</span><span className="afs-option-lib">InstantSearch.js</span></a></li>
      <li role="option" aria-selected="false"><a className="afs-option" href="/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/react"><span className="afs-option-name">React</span><span className="afs-option-lib">React InstantSearch</span></a></li>
      <li role="option" aria-selected="false"><a className="afs-option" href="/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/vue"><span className="afs-option-name">Vue</span><span className="afs-option-lib">Vue InstantSearch</span></a></li>
      <li role="option" aria-selected="true"><a className="afs-option is-current" href="/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/ios"><span className="afs-option-name">iOS</span><span className="afs-option-lib">InstantSearch iOS</span></a></li>
      <li role="option" aria-selected="false"><a className="afs-option" href="/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/android"><span className="afs-option-name">Android</span><span className="afs-option-lib">InstantSearch Android</span></a></li>
    </ul>
  </div>
</div>

<Info>
  Starting May 1, 2024,
  Apple requires all iOS apps to include a privacy manifest.
  For more information, see [Privacy manifest](/doc/guides/building-search-ui/resources/privacy-manifest/ios/).
</Info>

You may need to query Algolia's servers from the backend instead of the frontend, while still being able to reuse InstantSearch widgets.
Possible motivations could be for security restrictions, for SEO purposes, or to enrich the data sent by the custom server (fetch Algolia data and data from your servers).
Keep in mind though that you should use frontend search for performance and high availability reasons.

By the end of this guide, you will have learned how to use InstantSearch with your own backend architecture to query Algolia.
Even if you're not using Algolia on your backend and still want to benefit from using InstantSearch, then this guide is also for you.

## How InstantSearch works

InstantSearch offers reactive UI widgets that automatically update when there's a new search event.
Internally, it uses a `Searchable` interface that takes care of making network calls to get search results.
The most important method of that `Searchable` is a simple `search()` function that takes in a parameter that contains all the <SearchQuery /> parameters and then expects a callback to be called with the search results that you get from your backend.

## Basic implementation of using a custom backend

The most basic implementation of using a custom backend uses the `DefaultSearchClient` and requires you to implement just one method:
`search(query:searchResultsHandler:)`.
In this function, you use the query passed to you, make a network request to your backend server, transform the response into a `SearchResults` instance, and then finally call the `searchResultsHandler` callback with `searchResults`.
If there's an error, call the callback with the error.
Here is an example using the Alamofire networking library.

```swift Swift icon=code theme={"system"}
public class DefaultCustomBackend: DefaultSearchClient {
    override public func search(_ query: Query, searchResultsHandler: @escaping SearchResultsHandler) {
        // 1
        let queryText = query.query ?? ""

        // 2
        Alamofire.request("https://yourbackend.com/search?q=\(queryText)").responseJSON { responseJson in

            if let json = responseJson.result.value as? [String: Any] {
                do {
                    // 3
                    let searchResults = try SearchResults(content: json, disjunctiveFacets: [])

                    // 4
                    searchResultsHandler(searchResults, nil)
                } catch let error {
                    // 4
                    searchResultsHandler(nil, error)
                }
            }
        }
    }
}
```

This is the simplest example and will work only if on your backend,
you're calling Algolia and then just forwarding your result to the mobile app without doing any modification to the JSON data.

1. Get the query text from the query parameter that's passed in the method.
2. Make a request to your backend using the `queryText` parse in step 1.
3. Serialize your response into a `SearchResults` instance. If your response data is different than the original one returned by Algolia, especially if where you're not using Algolia at all in your backend, use a `SearchResults` initializer such as `SearchResults(nbHits:hits)`.
4. Call the `searchResultsHandler` function to instruct InstantSearch about the new search event, in this case the arrival of new search results, or an error.

### Integrate the custom backend

* To use the custom backend with InstantSearch core only (the `Searcher` class):

  ```swift Swift icon=code theme={"system"}
  let index = DefaultCustomBackend()
  let searcher = Searcher(index: index)
  ```

* To use the custom backend with InstantSearch with a single <Index />:

  ```swift Swift icon=code theme={"system"}
  let index = DefaultCustomBackend()
  let searcher = Searcher(index: index)
  let instantSearch = InstantSearch(searcher: searcher)
  ```

* To use the custom backend with InstantSearch with multiple indices:

  ```swift Swift icon=code theme={"system"}
  let productSearchable = CustomBackendProducts()
  let movieSearchable = CustomBackendMovies()
  instantSearch = InstantSearch.init(searchables: [productSearchable, movieSearchable], searcherIds: [SearcherId(index: "products"), SearcherId(index: "movie")])
  tableView.indices = "products,movie"
  ```

## Advanced implementation of using a custom backend

The above snippet only covers the case of doing a basic search of hits, with conjunctive (contrary to disjunctive) filtering. This section shows how to improve the structure of the custom backend class and support disjunctive faceting.

Start with this code snippet:

```swift Swift icon=code theme={"system"}
// 1
public struct BackendSearchParameters {
    var q: String?
    var disjunctiveFacets: [String]?
}
public struct BackendSearchResults {
    var total: Int
    var hits: [[String: Any]]
}

// 2
public class BackendImplementation: SearchClient<BackendSearchParameters, BackendSearchResults> {

    // 3
    public override func map(query: Query) -> BackendSearchParameters {
        let queryText = query.query

        return BackendSearchParameters(q: queryText, disjunctiveFacets: nil)
    }

    // 4
    public override func map(query: Query, disjunctiveFacets: [String], refinements: [String : [String]]) -> BackendSearchParameters {
        let queryText = query.query

        return BackendSearchParameters(q: queryText, disjunctiveFacets: disjunctiveFacets)
    }

    // 5
    public override func map(results: BackendSearchResults) -> SearchResults {
        let nbHits = results.total
        let hits = results.hits

        // 6
        let categoryFacet = ["chairs": 10, "tables": 15]
        let facets = ["category": categoryFacet]
        let extraContent = ["facets": facets]

        return SearchResults(nbHits: nbHits, hits: hits, extraContent: extraContent)
    }

    // 7
    public override func search(_ query: BackendSearchParameters, searchResultsHandler: @escaping SearchResultsHandler) {

        let queryText = query.q ?? ""

        Alamofire.request("https://yourbackend.com/search?q=\(queryText)").responseJSON { responseJson in

            if let json = responseJson.result.value as? [String: Any] {
                do {

                    let hitsJson = json["hits"] as! [String: Any]
                    let total = hitsJson["total"] as! Int
                    let hits = hitsJson["hits"] as! [[String: Any]]

                    let backendSearchResults = BackendSearchResults(total: total, hits: hits)

                    // 8
                    searchResultsHandler(backendSearchResults, nil)

                } catch let error {
                    searchResultsHandler(nil, error)
                }
            }
        }

    }
}
```

1. Create your models that will hold the query parameters and results that you need to make your custom backend call.
2. Create your class that inherits from `SearchClient`. Use the two models created in step 1 for the generics of that class. This will ensure strong typing and good practices throughout this implementation.
3. Implement the basic parameter mapper function that converts a query to your parameter model. Make sure you take all the fields you need from the query parameter.
4. Implement the advanced parameter mapper function. It's the same as the one used in step 3, but with two more parameters that you can use for your call: `disjunctiveFacets` and `refinements`.
5. Implement the result mapper function that converts your result model back to an Algolia `SearchResults` that can be understood by InstantSearch.
6. If you want to specify each <Facet /> for a refinement list, make sure you specify the `facets` property appropriately. Usually you'll want to get this data from your custom result model.
7. Implement the search method, same idea as the basic implementation. The only difference is that now it provides your custom parameter model as its parameter.
8. When you get new search results, you serialize them into your custom response model and then call the `searchResultsHandler` method.

### Integrate the custom backend

* To use the custom backend with InstantSearch core only (the `Searcher` class):

  ```swift Swift icon=code theme={"system"}
  let index = BackendImplementation()
  let searcher = Searcher(index: index)
  ```

* To use the custom backend with InstantSearch with a single index:

  ```swift Swift icon=code theme={"system"}
  let index = BackendImplementation()
  let searcher = Searcher(index: index)
  let instantSearch = InstantSearch(searcher: searcher)
  ```

* To use the custom backend with InstantSearch with multiple indices:

  ```swift Swift icon=code theme={"system"}
  let productSearchable = CustomBackendProducts()
  let movieSearchable = CustomBackendMovies()
  instantSearch = InstantSearch.init(searchables: [productSearchable, movieSearchable], searcherIds: [SearcherId(index: "products"), SearcherId(index: "movie")])
  tableView.indices = "products,movie"
  ```

## Trick to get more out of the query

One little trick you can use to get more detailed information about the `query` being passed as a parameter is to upcast it to a `SearchParameters` by doing

```swift Swift icon=code theme={"system"}
let searchParameters = query as! SearchParameters
```

In that way, you can access higher level properties like `disjunctiveFacets`, `facetRefinements`, `disjunctiveNumerics` and `numericRefinements`. This can be useful when transforming Algolia's Query.
