> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/ios/ja/search/llms.txt)

# Use Address Autofill to populate form fields

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/ios/ja/ios/ja/assets/medias/search-examples-address-autofill-f54a03b7fcf1a39b9d0179124d4747b4.mp4).

This example shows the **Mapbox Search SDK for iOS'** Address Autofill functionality to create a seamless address input and verification experience. The `AddressAutofillMainViewController` manages user input through a search bar and dynamically fetches address suggestions in real-time using the [`AddressAutofill`](https://docs.mapbox.com/ios/search/api/core/2.29.0/Classes/AddressAutofill.html) API. The results are displayed in a table view, where selecting a suggestion navigates to the `AddressAutofillResultViewController`. This interaction showcases the ability to autocomplete addresses, format them, and retrieve detailed results.

The `AddressAutofillResultViewController` visualizes the selected address with its components displayed in a table view and its location shown on a Mapbox map with annotations. Users can adjust the location interactively, triggering an updated autofill request to refine the result. The view state transitions between displaying the result, adjusting the location, and handling loading states, providing a responsive and user-friendly workflow. Together, these view controllers show a robust and interactive implementation of address autofill and validation using the Mapbox Search SDK.

> **Note: iOS Demo App Available**
> 
> This example code is part of the **Search SDK for iOS Demo App**, a working iOS project [available on Github](https://github.com/mapbox/mapbox-search-ios/tree/main/Demo). iOS developers are encouraged to run the demo app locally to interact with this example in a simulator and explore other features of the Search SDK.
> 
> The code below may depend on additional classes that are not part of the Search SDK itself, but are part of the demo app. You can find the full source code for the demo app in the [Mapbox Search iOS repository](https://github.com/mapbox/mapbox-search-ios/tree/main/)

**Swift**

Title: `AddressAutofillMainViewController.swift`

[View on GitHub](https://github.com/mapbox/mapbox-search-ios/blob/main/Demo/AddressAutofill/AddressAutofillMainViewController.swift)

```swift
import MapboxSearch
import UIKit

final class AddressAutofillMainViewController: UIViewController {
    @IBOutlet private var tableView: UITableView!
    @IBOutlet private var messageLabel: UILabel!

    private lazy var addressAutofill = AddressAutofill()

    private var cachedSuggestions: [AddressAutofill.Suggestion] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        configureUI()
    }
}

// MARK: - UISearchResultsUpdating

extension AddressAutofillMainViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard
            let text = searchController.searchBar.text,
            let query = AddressAutofill.Query(value: text)
        else {
            cachedSuggestions = []

            reloadData()
            return
        }

        addressAutofill.suggestions(for: query) { [weak self] result in
            guard let self else { return }

            switch result {
            case .success(let suggestions):
                cachedSuggestions = suggestions
                reloadData()

            case .failure(let error):
                print(error)
            }
        }
    }
}

// MARK: - UITableViewDataSource & UITableViewDelegate

extension AddressAutofillMainViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        cachedSuggestions.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "suggestion-tableview-cell"

        let tableViewCell: UITableViewCell = if let cachedTableViewCell = tableView
            .dequeueReusableCell(withIdentifier: cellIdentifier)
        {
            cachedTableViewCell
        } else {
            UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
        }

        let suggestion = cachedSuggestions[indexPath.row]

        tableViewCell.textLabel?.text = suggestion.name
        tableViewCell.accessoryType = .disclosureIndicator

        tableViewCell.detailTextLabel?.text = suggestion.formattedAddress
        tableViewCell.detailTextLabel?.textColor = UIColor.darkGray
        tableViewCell.detailTextLabel?.numberOfLines = 2

        return tableViewCell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        addressAutofill.select(suggestion: cachedSuggestions[indexPath.row]) { [weak self] result in
            switch result {
            case .success(let suggestionResult):
                let resultVC = AddressAutofillResultViewController.instantiate(with: suggestionResult)
                self?.navigationController?.pushViewController(resultVC, animated: true)

            case .failure(let error):
                print("Suggestion selection error \(error)")
            }
        }
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        60
    }
}

// MARK: - Private

extension AddressAutofillMainViewController {
    private func reloadData() {
        messageLabel.isHidden = !cachedSuggestions.isEmpty
        tableView.isHidden = cachedSuggestions.isEmpty

        tableView.reloadData()
    }

    private func configureUI() {
        configureSearchController()
        configureTableView()
        configureMessageLabel()
    }

    private func configureSearchController() {
        let searchController = UISearchController(searchResultsController: nil)
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = "Type your address"
        searchController.searchBar.returnKeyType = .done

        navigationItem.searchController = searchController
    }

    private func configureMessageLabel() {
        messageLabel
            .text =
            "Type at least \(AddressAutofill.Query.Requirements.queryLength) symbols to get Address Autofill suggestions"
    }

    private func configureTableView() {
        tableView.tableFooterView = UIView(frame: .zero)

        tableView.delegate = self
        tableView.dataSource = self

        tableView.isHidden = true
    }
}
```

**Swift**

Title: `AddressAutofillResultViewController.swift`

[View on GitHub](https://github.com/mapbox/mapbox-search-ios/blob/main/Demo/AddressAutofill/AddressAutofillResultViewController.swift)

```swift
import MapboxMaps
import MapboxSearch
import UIKit

final class AddressAutofillResultViewController: UIViewController {
    fileprivate enum ViewState {
        case result, adjusting, loading, empty
    }

    @IBOutlet private var tableView: UITableView!
    @IBOutlet private var mapView: MapView!
    @IBOutlet private var pinButton: UIButton!

    @IBOutlet private var activityView: UIView!
    @IBOutlet private var infoView: UIView!

    private var result: AddressAutofill.Result!
    private lazy var addressAutofill = AddressAutofill()
    private lazy var annotationsManager = mapView.annotations.makePointAnnotationManager()

    static func instantiate(with result: AddressAutofill.Result) -> AddressAutofillResultViewController {
        let storyboard = UIStoryboard(
            name: "Main",
            bundle: .main
        )

        let viewController = storyboard.instantiateViewController(
            withIdentifier: "AddressAutofillResultViewController"
        ) as? AddressAutofillResultViewController

        guard let viewController else {
            preconditionFailure()
        }

        viewController.result = result

        return viewController
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        prepare()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        showSuggestionRegion()
    }
}

// MARK: - TableView data source

extension AddressAutofillResultViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        result == nil ? .zero : result.addressComponents.all.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "result-cell"

        let tableViewCell: UITableViewCell = if let cachedTableViewCell = tableView
            .dequeueReusableCell(withIdentifier: cellIdentifier)
        {
            cachedTableViewCell
        } else {
            UITableViewCell(style: .value1, reuseIdentifier: cellIdentifier)
        }

        let addressComponent = result.addressComponents.all[indexPath.row]

        tableViewCell.textLabel?.text = addressComponent.kind.rawValue.capitalized
        tableViewCell.detailTextLabel?.text = addressComponent.value
        tableViewCell.detailTextLabel?.textColor = UIColor.darkGray

        return tableViewCell
    }
}

// MARK: - Private

extension AddressAutofillResultViewController {
    private func attachAdjustLocationButtonToNavigationItem() {
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            title: "Adjust",
            style: .plain,
            target: self,
            action: #selector(onStartAdjustLocationAction)
        )
    }

    private func attachDoneButtonToNavigationItem() {
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            title: "Done",
            style: .done,
            target: self,
            action: #selector(onFinishAdjustLocationAction)
        )
    }

    @objc
    private func onStartAdjustLocationAction() {
        result = nil
        annotationsManager.annotations = []

        updateViewState(to: .adjusting)
        attachDoneButtonToNavigationItem()
    }

    @objc
    private func onFinishAdjustLocationAction() {
        performAutofillRequest()
    }
}

// MARK: - Private

extension AddressAutofillResultViewController {
    private func prepare() {
        title = "Address"

        updateViewState(to: .result)

        attachAdjustLocationButtonToNavigationItem()
    }

    private func updateViewState(to viewState: ViewState) {
        switch viewState {
        case .result:
            mapView.isUserInteractionEnabled = false
            pinButton.isHidden = true
            activityView.isHidden = true
            infoView.isHidden = true

        case .adjusting:
            mapView.isUserInteractionEnabled = true
            pinButton.isHidden = false
            activityView.isHidden = true
            infoView.isHidden = false

        case .loading:
            mapView.isUserInteractionEnabled = false
            pinButton.isHidden = false
            activityView.isHidden = false
            infoView.isHidden = true

        case .empty:
            mapView.isUserInteractionEnabled = false
            pinButton.isHidden = true
            activityView.isHidden = true
            infoView.isHidden = true
        }

        updateScreenData()
    }

    private func updateScreenData() {
        guard let result else { return }

        showAnnotations(results: [result])
        showSuggestionRegion()

        tableView.reloadData()
    }

    private func showSuggestionRegion() {
        guard result != nil else { return }

        let cameraOptions = CameraOptions(
            center: result.coordinate,
            zoom: 10.5
        )

        mapView.camera.fly(to: cameraOptions, duration: 0.4)
    }

    private func performAutofillRequest() {
        result = nil

        updateViewState(to: .loading)

        let centerCoordinate = mapView.mapboxMap.coordinate(for: mapView.center)
        addressAutofill.suggestions(for: centerCoordinate) { [weak self] result in
            guard let self else { return }

            switch result {
            case .success(let suggestions):
                if let first = suggestions.first {
                    addressAutofill.select(suggestion: first) { [weak self] result in
                        guard let self else { return }

                        if case .success(let suggestionResult) = result {
                            self.result = suggestionResult
                            updateViewState(to: .result)
                        } else {
                            updateViewState(to: .empty)
                        }
                    }
                } else {
                    updateViewState(to: .empty)
                }

            case .failure(let error):
                debugPrint(error)

                updateViewState(to: .empty)
            }

            attachAdjustLocationButtonToNavigationItem()
        }
    }

    func showAnnotations(results: [AddressAutofill.Result], cameraShouldFollow: Bool = true) {
        annotationsManager.annotations = results.compactMap {
            PointAnnotation.pointAnnotation($0)
        }

        if cameraShouldFollow {
            cameraToAnnotations(annotationsManager.annotations)
        }
    }

    func cameraToAnnotations(_ annotations: [PointAnnotation]) {
        if annotations.count == 1, let annotation = annotations.first {
            mapView.camera.fly(
                to: .init(center: annotation.point.coordinates, zoom: 15),
                duration: 0.25,
                completion: nil
            )
        } else {
            do {
                let cameraState = mapView.mapboxMap.cameraState
                let coordinatesCamera = try mapView.mapboxMap.camera(
                    for: annotations.map(\.point.coordinates),
                    camera: CameraOptions(cameraState: cameraState),
                    coordinatesPadding: UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24),
                    maxZoom: nil,
                    offset: nil
                )

                mapView.camera.fly(to: coordinatesCamera, duration: 0.25, completion: nil)
            } catch {
                _Logger.searchSDK.error(error.localizedDescription)
            }
        }
    }
}
```