Skip to main content
The latest major version of the algoliasearch-client-swift package is version 9. This page helps you upgrade from version 8 and explains the breaking changes you need to address. Algolia generates the version 9 clients from OpenAPI specifications, which provides consistent behavior across all languages and up-to-date API coverage. The main architectural changes are:
  • The index(withName:) pattern is removed. All methods are now on the client instance directly, with indexName as a parameter.
  • Client initialization can throw, so try is required.
  • All API calls use Swift’s native async/await concurrency model.
  • All module names are prefixed with Algolia (for example, import AlgoliaSearch instead of import Search).
  • The client is compatible with Swift 6.
For the full list of changes, see the Swift changelog.

Update your dependencies

Swift Package Manager

Update your Swift Package Manager dependency to version 9. In your Package.swift, change the version requirement:
Swift
// version 8
.package(url: "https://github.com/algolia/algoliasearch-client-swift", from: "8.0.0")

// version 9
.package(url: "https://github.com/algolia/algoliasearch-client-swift", from: "9.0.0")
If you’re using Xcode, update the package version in File > Swift Packages.

CocoaPods

If you use CocoaPods, update your Podfile:
Ruby
# version 8
pod 'AlgoliaSearchClient', '~> 8.0'

# version 9
pod 'AlgoliaSearchClient', '~> 9.0.0'
With CocoaPods, all Algolia modules are bundled into a single AlgoliaSearchClient module. This means you use import AlgoliaSearchClient instead of the individual module imports shown in the rest of this guide.To support CocoaPods’ module flattening, some model names are prefixed with the client name to avoid conflicts. This applies regardless of your installation method. The prefixed names are more verbose, but the tradeoff is better autocompletion in IDEs and more predictable structure that works well with AI coding assistants.

Update imports

Since version 9.40.0, all module names are prefixed with Algolia to avoid conflicts with other dependencies. The main module changed from AlgoliaSearchClient to AlgoliaSearch:
Swift
// version 8
import AlgoliaSearchClient

// version 9
import AlgoliaCore
import AlgoliaSearch
Most version 9 code requires AlgoliaCore alongside the API-specific module, since AlgoliaCore defines shared types such as SearchClient configuration and request options. Version 9 includes dedicated modules for each API. To access methods from a specific API, import the corresponding module:
Swift
// Search API
import AlgoliaSearch
// Recommend API
import AlgoliaRecommend
// A/B testing API
import AlgoliaAbtesting
// Analytics API
import AlgoliaAnalytics
// Personalization API
import AlgoliaPersonalization
// Query Suggestions API
import AlgoliaQuerySuggestions

Update client initialization

In version 9, SearchClient initialization can throw, requiring the try keyword:
Swift
// version 8
let client = SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")

// version 9
let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")
The try keyword is required because version 9 validates your application ID and API key during initialization. If you’re initializing the client inside a function that doesn’t already throw, wrap it in a do/catch block.
The other major change concerns what follows initialization: index(withName:) no longer exists.

Remove index(withName:)

This is the most significant change when upgrading. Version 8 relied on an index object with methods called on it. In version 9, all methods belong to the client instance, with indexName as a parameter.
Swift
// version 8
let client = SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")
let index = client.index(withName: "INDEX_NAME")
index.search(query: "QUERY")

// version 9
let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")
try await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchSearchParams.searchSearchParamsObject(
        SearchSearchParamsObject(query: "QUERY")
    )
)
If you have many files to update, search your codebase for index(withName:) or .index(withName: to find every place that needs changing.

Add async/await

Version 9 is built on Swift’s native concurrency model. All API calls are async functions that require try await. In version 8, API calls rely on completion handlers:
Swift
// version 8 -- completion handler
let index = client.index(withName: "INDEX_NAME")
index.search(query: Query("QUERY")) { result in
    switch result {
    case .success(let response):
        print(response.hits)
    case .failure(let error):
        print(error)
    }
}

// version 9 -- async/await
let response: SearchResponse<Hit> = try await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchSearchParams.searchSearchParamsObject(
        SearchSearchParamsObject(query: "QUERY")
    )
)
If you’re calling Algolia methods from synchronous code, wrap them in a Task:
Swift
// version 9 -- calling from synchronous code
Task {
    do {
        let response: SearchResponse<Hit> = try await client.searchSingleIndex(
            indexName: "INDEX_NAME",
            searchParams: SearchSearchParams.searchSearchParamsObject(
                SearchSearchParamsObject(query: "QUERY")
            )
        )
        print(response.hits)
    } catch {
        print(error)
    }
}

Update search calls

Search a single index

The index.search() method is now client.searchSingleIndex(). Pass the index name and search parameters as named arguments:
Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
let result = index.search(query: "QUERY")

// version 9
let result: SearchResponse<Hit> = try await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchSearchParams.searchSearchParamsObject(
        SearchSearchParamsObject(
            query: "QUERY",
            facetFilters: SearchFacetFilters.arrayOfSearchFacetFilters(
                [SearchFacetFilters.string("category:Book")]
            )
        )
    )
)

Search multiple indices

The client.multipleQueries() method is now client.search(). Each request in the array requires an indexName:
Swift
// version 8
let results = client.multipleQueries(queries: [
    IndexQuery(indexName: "INDEX_1", query: Query("QUERY")),
    IndexQuery(indexName: "INDEX_2", query: Query("QUERY")),
])

// version 9
let response: SearchResponses<Hit> = try await client.search(
    searchMethodParams: SearchMethodParams(
        requests: [
            SearchQuery.searchForHits(SearchForHits(
                indexName: "INDEX_1",
                query: "QUERY"
            )),
            SearchQuery.searchForHits(SearchForHits(
                indexName: "INDEX_2",
                query: "QUERY"
            )),
        ]
    )
)

Search for facet values

The index.searchForFacetValues() method becomes client.searchForFacetValues() with an indexName parameter:
Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
let results = index.searchForFacetValues(of: "category", matching: "book")

// version 9
let results = try await client.searchForFacetValues(
    indexName: "INDEX_NAME",
    facetName: "category",
    searchForFacetValuesRequest: SearchForFacetValuesRequest(facetQuery: "book")
)

Update indexing operations

In version 9, indexing methods are on the client instead of the index object, with indexName as a parameter.

Add or replace records

Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.saveObject(["objectID": "1", "name": "Record"])

// version 9
let response = try await client.saveObject(
    indexName: "INDEX_NAME",
    body: [
        "objectID": "1",
        "name": "Record",
    ]
)
// saveObjects works the same way:
let response = try await client.saveObjects(
    indexName: "INDEX_NAME",
    objects: [["objectID": "1", "name": "Record"]]
)

Partially update records

Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.partialUpdateObject(["objectID": "1", "name": "Updated"])

// version 9
let response = try await client.partialUpdateObject(
    indexName: "INDEX_NAME",
    objectID: "1",
    attributesToUpdate: ["name": "Updated"]
)

Delete records

Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.deleteObject(withID: "1")

// version 9
let response = try await client.deleteObject(indexName: "INDEX_NAME", objectID: "1")

Update settings, synonyms, and rules

Get and set settings

Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
let settings = index.getSettings()
index.setSettings(Settings().set(\.searchableAttributes, to: ["title", "author"]))

// version 9
let settings = try await client.getSettings(
    indexName: "INDEX_NAME"
)
try await client.setSettings(
    indexName: "INDEX_NAME",
    indexSettings: IndexSettings(searchableAttributes: ["title", "author"])
)

Save synonyms and rules

Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.saveSynonyms([Synonym.regular(objectID: "1", synonyms: ["car", "auto"])])
index.saveRules([Rule(objectID: "1")])

// version 9
try await client.saveSynonyms(
    indexName: "INDEX_NAME",
    synonymHit: [SynonymHit(objectID: "1", type: SynonymType.synonym, synonyms: ["car", "auto"])]
)
try await client.saveRules(
    indexName: "INDEX_NAME",
    rules: [Rule(
        objectID: "1",
        conditions: [SearchCondition(pattern: "shoes", anchoring: SearchAnchoring.contains)],
        consequence: SearchConsequence(params: SearchConsequenceParams(filters: "brand:nike"))
    )]
)
In version 8, index.replaceAllRules() and index.replaceAllSynonyms() replaced all rules or synonyms. In version 9, use client.saveRules() or client.saveSynonyms() with the clearExistingRules or replaceExistingSynonyms parameter set to true.

Update index management

The copyIndex, moveIndex, copyRules, copySynonyms, and copySettings methods are all replaced by a single operationIndex method.

Copy an index

Swift
// version 8
client.copyIndex(from: "SOURCE_INDEX_NAME", to: "DESTINATION_INDEX_NAME")

// version 9
try await client.operationIndex(
    indexName: "SOURCE_INDEX_NAME",
    operationIndexParams: OperationIndexParams(
        operation: OperationType.copy,
        destination: "DESTINATION_INDEX_NAME"
    )
)

Move (rename) an index

Swift
// version 8
client.moveIndex(from: "SOURCE_INDEX_NAME", to: "DESTINATION_INDEX_NAME")

// version 9
try await client.operationIndex(
    indexName: "SOURCE_INDEX_NAME",
    operationIndexParams: OperationIndexParams(
        operation: OperationType.move,
        destination: "DESTINATION_INDEX_NAME"
    )
)

Copy only rules or settings

In version 9, use the scope parameter to limit the operation to specific data:
Swift
// version 9 -- copy only rules and settings from one index to another
try await client.operationIndex(
    indexName: "SOURCE_INDEX_NAME",
    operationIndexParams: OperationIndexParams(
        operation: OperationType.copy,
        destination: "DESTINATION_INDEX_NAME",
        scope: [ScopeType.rules, ScopeType.settings]
    )
)

Check if an index exists

In version 8, you could check if an index existed using the exists method on the index object. In version 9, use the indexExists helper method on the client:
Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.exists()

// version 9
let response = try await client.indexExists(indexName: "INDEX_NAME")

Update task handling

Version 8 supported chaining .wait() on operations. Version 9 replaces this pattern with dedicated wait helpers.
Swift
// version 8
let index = client.index(withName: "INDEX_NAME")
index.saveObjects(records).wait()

// version 9
let response = try await client.saveObjects(
    indexName: "INDEX_NAME",
    objects: records
)
try await client.waitForTask(indexName: "INDEX_NAME", taskID: Int64(response.taskID))
Version 9 includes three wait helpers:

Helper method changes

The following sections document breaking changes in helper method signatures and behavior between version 8 and version 9.

replaceAllObjects

The safe parameter has been removed. In version 8, safe: true caused the helper to wait after each step. In version 9, the helper always waits—equivalent to the previous safe: true behavior. The completion-handler-based API has also been removed in favor of Swift concurrency (async/await).
Swift
// version 8
index.replaceAllObjects(with: objects, safe: true) { result in
    // handle result
}

// version 9
let response = try await client.replaceAllObjects(
    indexName: "INDEX_NAME",
    objects: objects,
    scopes: [.settings, .rules, .synonyms]
)

saveObjects

The autoGeneratingObjectID parameter has been removed. In version 9, every object must include an objectID. The completion-handler-based API has also been removed in favor of Swift concurrency.
Swift
// version 8
index.saveObjects(objects, autoGeneratingObjectID: true) { result in
    // handle result
}

// version 9
// Objects must include objectID, or use chunkedBatch with .addObject action
let response = try await client.saveObjects(indexName: "INDEX_NAME", objects: objects)

deleteObjects

The input type changed from [ObjectID] (a typed wrapper) to [String]. The helper moved to the client, and two new optional parameters are available: waitForTasks (default false) and batchSize (default 1,000).
Swift
// version 8
index.deleteObjects(withIDs: [ObjectID("id1"), ObjectID("id2")]) { result in
    // handle result
}

// version 9
try await client.deleteObjects(
    indexName: "INDEX_NAME",
    objectIDs: ["id1", "id2"],
    waitForTasks: true
)

partialUpdateObjects

The input type changed from [(objectID: ObjectID, update: PartialUpdate)] to [some Encodable]. The createIfNotExists default flipped from true (version 8) to false (version 9). The completion-handler API was also replaced with Swift concurrency.
Swift
// version 8
// createIfNotExists defaulted to true
index.partialUpdateObjects(
    updates: [(ObjectID("id1"), .set(attribute: "name", value: .string("new")))],
    createIfNotExists: false
) { result in
    // handle result
}

// version 9
// createIfNotExists defaults to false — set explicitly if you relied on the old default
try await client.partialUpdateObjects(
    indexName: "INDEX_NAME",
    objects: myObjects,
    createIfNotExists: true
)

browseObjects, browseRules, browseSynonyms

In version 8, these helpers eagerly fetched all pages and returned the full result array via a completion handler or throwing call. In version 9, they accept an aggregator closure invoked with each page, use Swift concurrency, and accept an optional validate closure to stop early.
Swift
// version 8
index.browseObjects() { result in
    switch result {
    case .success(let responses):
        let allHits = responses.flatMap { $0.hits }
    case .failure(let error):
        // handle error
    }
}

// version 9
var objects: [Hit] = []

try await client.browseObjects(
    indexName: "INDEX_NAME",
    browseParams: BrowseParamsObject(),
    aggregator: { response in
        objects.append(contentsOf: response.hits)
    }
)

generateSecuredApiKey and getSecuredApiKeyRemainingValidity

Both method names changed from API (all caps) to Api (title case) to follow Swift naming conventions.
Swift
// version 8
let key = SearchClient.generateSecuredAPIKey(
    withParent: "parentApiKey",
    parameters: SecuredAPIKeyRestrictions(validUntil: 1893456000)
)
let remaining = SearchClient.getSecuredAPIKeyRemainingValidity(of: key)

// version 9
let key = try SearchClient.generateSecuredApiKey(
    parentApiKey: "parentApiKey",
    restrictions: SecuredApiKeyRestrictions(validUntil: 1893456000)
)
let remaining = try SearchClient.getSecuredApiKeyRemainingValidity(for: key)

waitForTask

The helper was renamed from waitTask to waitForTask, moved to the client, and now uses Swift concurrency. The timeout parameter changed from TimeInterval? (a maximum wall-clock limit) to a retry-count-to-delay closure (exponential backoff). An explicit maxRetries parameter (default 50) was added.
Swift
// version 8
try index.waitTask(withID: taskID, timeout: 30)

// version 9
try await client.waitForTask(
    indexName: "INDEX_NAME",
    taskID: taskID,
    maxRetries: 50
)

waitForAppTask

This is a new helper in version 9.
Swift
try await client.waitForAppTask(taskID: taskID)

waitForApiKey

This is a new standalone helper in version 9.
Swift
// Wait for a key to be created:
try await client.waitForApiKey(key: "my-api-key", operation: .add)

// Wait for a key update (pass the expected final state):
try await client.waitForApiKey(
    key: "my-api-key",
    operation: .update,
    apiKey: ApiKey(acl: [.search])
)

indexExists

This helper is new in version 9.
Swift
let exists = try await client.indexExists(indexName: "INDEX_NAME")

chunkedBatch

chunkedBatch is now a public helper in version 9. In version 8, chunking was an internal detail of saveObjects. The action parameter defaults to .addObject and waitForTasks defaults to false.
Swift
let responses = try await client.chunkedBatch(
    indexName: "INDEX_NAME",
    objects: myObjects,
    action: .addObject,
    waitForTasks: true
)

copyIndexBetweenApplications

In version 8, the AccountClient struct provided a static copyIndex(source:destination:) method for copying an index between two different Algolia applications. In version 9, AccountClient is removed. You can compose existing helpers across two clients to achieve the same result.
Swift
// version 8
let tasks = try AccountClient.copyIndex(source: srcIndex, destination: destIndex)

// version 9
let src = try SearchClient(appID: "SRC_APP_ID", apiKey: "SRC_API_KEY")
let dst = try SearchClient(appID: "DST_APP_ID", apiKey: "DST_API_KEY")

// Copy settings
let settings = try await src.getSettings(indexName: "SOURCE_INDEX")
try await dst.setSettings(indexName: "DEST_INDEX", indexSettings: settings)

// Copy rules
var rules: [Rule] = []
try await src.browseRules(indexName: "SOURCE_INDEX") { rules.append(contentsOf: $0.hits) }
if !rules.isEmpty {
    try await dst.saveRules(indexName: "DEST_INDEX", rules: rules)
}

// Copy synonyms
var synonyms: [SynonymHit] = []
try await src.browseSynonyms(indexName: "SOURCE_INDEX") { synonyms.append(contentsOf: $0.hits) }
if !synonyms.isEmpty {
    try await dst.saveSynonyms(indexName: "DEST_INDEX", synonyms: synonyms)
}

// Copy objects
var objects: [MyModel] = []
try await src.browseObjects(indexName: "SOURCE_INDEX") { objects.append(contentsOf: $0.hits) }
try await dst.replaceAllObjects(indexName: "DEST_INDEX", objects: objects)

Method changes reference

The following tables list all method names that changed between version 8 and version 9.

Search API client

Version 8 (legacy)Version 9 (current)
client.addAPIKeyclient.addApiKey
client.addAPIKey.waitclient.waitForApiKey
client.clearDictionaryEntriesclient.batchDictionaryEntries
client.copyIndexclient.operationIndex
client.copyRulesclient.operationIndex
client.copySynonymsclient.operationIndex
client.deleteAPIKeyclient.deleteApiKey
client.deleteDictionaryEntriesclient.batchDictionaryEntries
client.generateSecuredAPIKeyclient.generateSecuredApiKey
client.getAPIKeyclient.getApiKey
client.getSecuredAPIKeyRemainingValidityclient.getSecuredApiKeyRemainingValidity
client.listAPIKeysclient.listApiKeys
client.listIndicesclient.listIndices
client.moveIndexclient.operationIndex
client.batchclient.multipleBatch
client.multipleQueriesclient.search
client.replaceDictionaryEntriesclient.batchDictionaryEntries
client.restoreAPIKeyclient.restoreApiKey
client.saveDictionaryEntriesclient.batchDictionaryEntries
client.updateAPIKeyclient.updateApiKey
index.batchclient.batch
index.browseclient.browseObjects
index.browseRulesclient.browseRules
index.browseSynonymsclient.browseSynonyms
index.clearObjectsclient.clearObjects
index.clearRulesclient.clearRules
index.clearSynonymsclient.clearSynonyms
index.copySettingsclient.operationIndex
index.deleteclient.deleteIndex
index.deleteByclient.deleteBy
index.deleteObjectclient.deleteObject
index.deleteObjectsclient.deleteObjects
index.deleteRuleclient.deleteRule
index.deleteSynonymclient.deleteSynonym
index.existsclient.indexExists
index.findObjectclient.searchSingleIndex
index.getObjectclient.getObject
index.getObjectsclient.getObjects
index.getRuleclient.getRule
index.getSettingsclient.getSettings
index.getSynonymclient.getSynonym
index.getTaskclient.getTask
index.partialUpdateObjectclient.partialUpdateObject
index.partialUpdateObjectsclient.partialUpdateObjects
index.replaceAllObjectsclient.replaceAllObjects
index.replaceAllRulesclient.saveRules
index.replaceAllSynonymsclient.saveSynonyms
index.saveObjectclient.saveObject
index.saveObjectsclient.saveObjects
index.saveRuleclient.saveRule
index.saveRulesclient.saveRules
index.saveSynonymclient.saveSynonym
index.saveSynonymsclient.saveSynonyms
index.searchclient.searchSingleIndex
index.searchForFacetValuesclient.searchForFacetValues
index.searchRulesclient.searchRules
index.searchSynonymsclient.searchSynonyms
index.setSettingsclient.setSettings
index.{operation}.waitclient.waitForTask

Recommend API client

Version 8 (legacy)Version 9 (current)
client.getFrequentlyBoughtTogetherclient.getRecommendations
client.getRecommendationsclient.getRecommendations
client.getRelatedProductsclient.getRecommendations
Last modified on April 22, 2026