Search with geocode

View on GitHub

Find the location for an address, or places of interest near a location or within a specific area.

Screenshot for search with geocode sample

Use case

A user can input a raw address into the app's search bar and zoom to the address location. When getting directions or looking for nearby places, users may only know what the place has ("food"), the type of place ("gym"), or the generic place name ("Starbucks"), rather than the specific address. You can get suggestions and locations for these places of interest (POIs) using a natural language query. Additionally, you can filter the results to a specific area.

How to use the sample

Choose an address from the suggestions or submit your own address to show its location on the map in a callout. Tap on a result pin to display its address. If you pan away from the result area, a "Repeat Search Here" button will appear. Tap it to query again for the currently viewed area on the map.

How it works

  1. Create a LocatorSearchSource using the toolkit.
  2. Create a SearchView using the toolkit with the locator search source.
  3. Perform a search. Identify a result pin graphic in the graphics overlay and show a callout with its address.

Relevant API

  • ArcGISToolkit.LocatorSearchSource
  • ArcGISToolkit.SearchView
  • GeocodeParameters
  • LocatorTask
  • SearchResult
  • SearchSuggestion
  • SuggestParameters

Additional information

This sample uses the World Geocoding Service. For more information, see Geocoding service from ArcGIS Developer website.

Tags

address, businesses, geocode, locations, locator, places of interest, POI, point of interest, search, suggestions, toolkit

Sample Code

SearchWithGeocodeView.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2022 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SwiftUI
import ArcGIS
import ArcGISToolkit

struct SearchWithGeocodeView: View {
    /// The viewpoint used by the search view to pan/zoom the map to the extent
    /// of the search results.
    @State private var viewpoint: Viewpoint? = Viewpoint(
        center: Point(
            x: -93.258133,
            y: 44.986656,
            spatialReference: .wgs84
        ),
        scale: 1e6
    )

    /// Denotes whether the map view is navigating. Used for the repeat search
    /// behavior.
    @State private var isGeoViewNavigating = false

    /// The current map view extent. Used to allow repeat searches after
    /// panning/zooming the map.
    @State private var geoViewExtent: Envelope?

    /// The center for the search.
    @State private var queryCenter: Point?

    /// The screen point to perform an identify operation.
    @State private var identifyScreenPoint: CGPoint?

    /// The tap location to perform an identify operation.
    @State private var identifyTapLocation: Point?

    /// The placement for a graphic callout.
    @State private var calloutPlacement: CalloutPlacement?

    /// Provides search behavior customization.
    @ObservedObject private var locatorDataSource = LocatorSearchSource(
        name: "My Locator",
        maximumResults: 10,
        maximumSuggestions: 5
    )

    /// The view model for the sample.
    @StateObject private var model = Model()

    var body: some View {
        MapViewReader { proxy in
            MapView(
                map: model.map,
                viewpoint: viewpoint,
                graphicsOverlays: [model.searchResultsOverlay]
            )
            .onSingleTapGesture { screenPoint, tapLocation in
                identifyScreenPoint = screenPoint
                identifyTapLocation = tapLocation
            }
            .onNavigatingChanged { isGeoViewNavigating = $0 }
            .onViewpointChanged(kind: .centerAndScale) {
                queryCenter = $0.targetGeometry.extent.center
            }
            .onVisibleAreaChanged { newVisibleArea in
                // For "Repeat Search Here" behavior, use `geoViewExtent` and
                // `isGeoViewNavigating` modifiers on the search view.
                geoViewExtent = newVisibleArea.extent
            }
            .callout(placement: $calloutPlacement.animation()) { placement in
                // Show the address of user tapped location graphic.
                // To get the fully geocoded address, use "Place_addr".
                Text(placement.geoElement?.attributes["Match_addr"] as? String ?? "Unknown Address")
                    .padding()
            }
            .task(id: identifyScreenPoint) {
                guard let screenPoint = identifyScreenPoint,
                      // Identifies when user taps a graphic.
                      let identifyResult = try? await proxy.identify(
                        on: model.searchResultsOverlay,
                        screenPoint: screenPoint,
                        tolerance: 10
                      )
                else {
                    return
                }
                // Creates a callout placement at the user tapped location.
                calloutPlacement = identifyResult.graphics.first.flatMap { graphic in
                    CalloutPlacement.geoElement(graphic, tapLocation: identifyTapLocation)
                }
                identifyScreenPoint = nil
                identifyTapLocation = nil
            }
            .overlay {
                SearchView(
                    sources: [locatorDataSource],
                    viewpoint: $viewpoint
                )
                .resultsOverlay(model.searchResultsOverlay)
                .queryCenter($queryCenter)
                .geoViewExtent($geoViewExtent)
                .isGeoViewNavigating($isGeoViewNavigating)
                .onQueryChanged { query in
                    if query.isEmpty {
                        // Hides the callout when query is cleared.
                        calloutPlacement = nil
                    }
                }
                .padding()
            }
        }
    }
}

private extension SearchWithGeocodeView {
    /// The model used to store the geo model and other expensive objects
    /// used in this view.
    class Model: ObservableObject {
        /// A map with imagery basemap.
        let map = Map(basemapStyle: .arcGISImagery)

        /// The graphics overlay used by the search toolkit component to display
        /// search results on the map.
        let searchResultsOverlay = GraphicsOverlay()
    }
}

#Preview {
    SearchWithGeocodeView()
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.