Observable GeoCoder

In this post I’ll show you how to make an Observable Geocoder to use in your #SwiftUI projects.

Firstly we want to import CoreLocation for the location services.

We’re going to construct a basic class, which subclasses ObservableObject.

class Geocoder: ObservableObject {
/// ...

We’re going to add @Published public var (published public variables), for the items we want to expose, in my case it are the following variables placeMark, location, name, iso, country, postalCode, state, subState, city, subCity, street, subStreet, region, timeZone, inlandWater, ocean and areasOfInterest.

Full Code:

import CoreLocation

class Geocoder: ObservableObject {
    /// Placemark
    @Published public var placeMark: CLPlacemark?

    /// Location
    @Published public var location: CLLocation?

    /// The name of the placemark.
    @Published public var name: String?

    /// The abbreviated country or region name.
    @Published public var iso: String?

    /// The name of the country or region associated with the placemark.
    @Published public var country: String?

    /// The postal code associated with the placemark.
    @Published public var postalCode: String?

    /// The state or province associated with the placemark.
    @Published public var state: String?

    /// Additional administrative area information for the placemark.
    @Published public var subState: String?

    /// The city associated with the placemark.
    @Published public var city: String?

    /// Additional city-level information for the placemark.
    @Published public var subCity: String?

    /// The street address associated with the placemark.
    @Published public var street: String?

    /// Additional street-level information for the placemark.
    @Published public var subStreet: String?

    /// The geographic region associated with the placemark.
    @Published public var region: CLRegion?

    /// The time zone associated with the placemark.
    @Published public var timeZone: TimeZone?

    /// The name of the inland water body associated with the placemark.
    @Published public var inlandWater: String?

    /// The name of the ocean associated with the placemark.
    @Published public var ocean: String?

    /// The relevant areas of interest associated with the placemark.
    @Published public var areasOfInterest: [String]?

    /// Geocoder
    private let geoCoder = CLGeocoder()

    init () { }

    /// Update to location
    func update (to location: CLLocation) {
            completionHandler: { (placemarks, _) -> Void in
                // We're only using the first place mark.
                if let placeMark = placemarks?[0] {
                    self.placeMark = placeMark
                    self.location = placeMark.location
                    self.name = placeMark.name
                    self.iso = placeMark.isoCountryCode
                    self.country = placeMark.country
                    self.postalCode = placeMark.postalCode
                    self.state = placeMark.administrativeArea
                    self.subState = placeMark.subAdministrativeArea
                    self.city = placeMark.locality
                    self.subCity = placeMark.subLocality
                    self.street = placeMark.thoroughfare
                    self.subStreet = placeMark.subThoroughfare
                    self.region = placeMark.region
                    self.timeZone = placeMark.timeZone
                    self.inlandWater = placeMark.inlandWater
                    self.ocean = placeMark.ocean
                    self.areasOfInterest = placeMark.areasOfInterest

                    // Send a notification that our `@Published` values have been changed.


//  _T.swift
//  Created by Wesley de Groot on 25/11/2022.

import SwiftUI

class someView: View {
    @State var isPresenting = false
    @StateObject var geocoder: Geocoder = .init()

    var body: some View {
        NavigationView {
            VStack {
                    mapType: .mutedStandard,
                    region: region,
                ) { annotation in
                    if let annotation {
                        self.isPresenting = true
                        geocoder.update(to: .init(
                            latitude: annotation.coordinate.latitude,
                            longitude: annotation.coordinate.longitude
                    } else {
                        // Deselected pin
                        self.isPresenting = false
                    isPresented: $isPresenting,
                    detents: [.medium()]
                ) {
                    VStack {
                        Text("@Observed Geocoder demo")
                        GroupBox("Pin location") {
                            VStack {
                                CustomRow(title: "Name", value: geocoder.name)
                                CustomRow(title: "ISO", value: geocoder.iso)
                                CustomRow(title: "Country", value: geocoder.country)
                                CustomRow(title: "Postal", value: geocoder.postalCode)
                                CustomRow(title: "State", value: geocoder.state)
                                CustomRow(title: "SubState", value: geocoder.subState)
                                CustomRow(title: "City", value: geocoder.city)
                                CustomRow(title: "SubCity", value: geocoder.subCity)
                            VStack {
                                CustomRow(title: "Street", value: geocoder.street)
                                CustomRow(title: "SubStreet", value: geocoder.subStreet)
                                CustomRow(title: "Region", value: "...")
                                CustomRow(title: "timeZone", value: geocoder.timeZone?.identifier)
                                CustomRow(title: "inlandWater", value: geocoder.inlandWater)
                                CustomRow(title: "ocean", value: geocoder.ocean)
                                CustomRow(title: "areasOfInterest",
                                          value: geocoder.areasOfInterest?.joined(separator: "; ")

struct CustomRow: View {
    let title: String
    let value: String?

    var body: some View {
        HStack {


            Text(value ?? "Unknown")

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.