Find address with reverse geocode

View on GitHub

Use an online service to find the address for a tapped point.

Image of find address with reverse geocode

Use case

You might use a geocoder to find a customer's delivery address based on the location returned by their device's GPS.

How to use the sample

Tap the map to see the nearest address displayed in a callout.

How it works

  1. Create a LocatorTask object using a URL to a geocoder service.
  2. Create an instance of ReverseGeocodeParameters and set ReverseGeocodeParameters.maxResults to 1.
  3. Pass the ReverseGeocodeParameters into LocatorTask.reverseGeocode(forLocation:parameters:) and get the matching results from the GeocodeResult.
  4. Show the results using a PictureMarkerSymbol and add the symbol to a Graphic in the GraphicsOverlay.

Relevant API

  • GeocodeResult
  • LocatorTask
  • ReverseGeocodeParameters

Additional information

This sample uses the World Geocoding Service. For more information, see the Geocoding service help topic on the Esri Developer website.

Tags

address, geocode, locate, reverse geocode, search

Sample Code

FindAddressWithReverseGeocodeView.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2023 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 ArcGIS
import SwiftUI

struct FindAddressWithReverseGeocodeView: View {
    /// The view model for the sample.
    @StateObject private var model = Model()

    /// The point on the map where the user tapped.
    @State private var tapLocation: Point?

    /// The placement of the callout on the map.
    @State private var calloutPlacement: CalloutPlacement?

    /// The text shown in the callout.
    @State private var calloutText: String?

    /// The error shown in the error alert.
    @State private var error: Error?

    var body: some View {
        MapView(map: model.map, graphicsOverlays: [model.graphicsOverlay])
            .callout(placement: $calloutPlacement.animation(.default.speed(2))) { _ in
                Text(calloutText ?? "No address found.")
                    .font(.callout)
                    .padding(8)
            }
            .onSingleTapGesture { _, mapPoint in
                tapLocation = mapPoint
            }
            .task(id: tapLocation) {
                guard let tapLocation else { return }
                await reverseGeocode(tapLocation)
            }
            .task {
                do {
                    try await model.locatorTask.load()
                } catch {
                    self.error = error
                }
            }
            .overlay(alignment: .top) {
                Text("Tap on the map to get the address for point.")
                    .frame(maxWidth: .infinity, alignment: .center)
                    .padding(8)
                    .background(.thinMaterial, ignoresSafeAreaEdges: .horizontal)
            }
            .errorAlert(presentingError: $error)
    }

    /// Reverse geocodes a given point and updates the marker with the result.
    /// - Parameter mapPoint: The point on the map to reverse geocode.
    private func reverseGeocode(_ mapPoint: Point) async {
        //  Normalize the map point.
        guard let normalizedPoint = GeometryEngine.normalizeCentralMeridian(
            of: mapPoint
        ) as? Point else { return }

        do {
            // Perform reverse geocode using the locator task with the point and parameters.
            let geocodeResults = try await model.locatorTask.reverseGeocode(
                forLocation: normalizedPoint,
                parameters: model.reverseGeocodeParameters
            )

            // Update the callout text using the first result from the reverse geocode.
            updateCalloutText(using: geocodeResults.first)
        } catch {
            self.error = error
            calloutText = nil
        }

        // Update the marker and callout location.
        model.markerGraphic.geometry = normalizedPoint
        calloutPlacement = .geoElement(model.markerGraphic, tapLocation: normalizedPoint)
    }

    /// Updates the callout text using the address from a given geocode result.
    /// - Parameter geocodeResult: The result to get the address from.
    private func updateCalloutText(using geocodeResult: GeocodeResult?) {
        // Get the address from the result's attributes.
        let addressText = model.reverseGeocodeParameters.resultAttributeNames
            .compactMap { geocodeResult?.attributes[$0] as? String }
            .filter { !$0.isEmpty }
            .joined(separator: ", ")

        // Update the callout text if an address was found.
        calloutText = !addressText.isEmpty ? addressText : nil
    }
}

private extension FindAddressWithReverseGeocodeView {
    /// The model used to store the geo model and other expensive objects used in this view.
    class Model: ObservableObject {
        /// A map with a topographic basemap initially centered on Redlands, CA, USA.
        let map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(
                center: Point(x: -117.195, y: 34.058, spatialReference: .wgs84),
                scale: 5e4
            )
            return map
        }()

        /// The graphics overlay for the marker graphic.
        let graphicsOverlay = GraphicsOverlay()

        /// The red map marker graphic used to indicate a tap location on the map.
        let markerGraphic = {
            // Create a symbol using the image from the project assets.
            let markerImage = UIImage.redMarker
            let markerSymbol = PictureMarkerSymbol(image: .redMarker)

            // Change the symbol's offsets, so it aligns properly to a given point.
            markerSymbol.leaderOffsetY = markerImage.size.height / 2
            markerSymbol.offsetY = markerImage.size.height / 2

            // Create a graphic with the symbol.
            return Graphic(symbol: markerSymbol)
        }()

        /// The locator task for reverse geocoding.
        let locatorTask = LocatorTask(url: .geocodeServer)

        /// The parameters for the reverse geocode operation.
        let reverseGeocodeParameters = {
            let parameters = ReverseGeocodeParameters()
            parameters.addResultAttributeNames(["Address", "City", "RegionAbbr"])
            parameters.maxResults = 1
            return parameters
        }()

        init() {
            graphicsOverlay.addGraphic(markerGraphic)
        }
    }
}

private extension URL {
    /// A URL to a geocode server on ArcGIS Online.
    static var geocodeServer: URL {
        URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")!
    }
}

#Preview {
    FindAddressWithReverseGeocodeView()
}

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