With iOS 26, Apple deprecated CLGeocoder — the API developers have relied on since Core Location’s earliest days to turn a coordinate into a readable place: city, region, nearby point of interest. The replacement lives in MapKit: MKReverseGeocodingRequest.1

Let’s take one coordinate as an example, about 20 metres (60 feet) away from Cypress Mountain Resort’s main cabin, located in the city of West Vancouver, BC in Canada:

Coordinates `49.396840, -123.204116`, located 20 metres from Cypress Mountain Resort, in West Vancouver, BC, Canada.
Coordinates `49.396840, -123.204116`, located 20 metres from Cypress Mountain Resort, in West Vancouver, BC, Canada.

Below is the code I ran both reverse-geocoding processes, side by side:

let location = CLLocation(latitude: 49.396840, longitude: -123.204116)

func geocoderLookup() async -> String {
    let coder = CLGeocoder()
    if let placemarks = try? await coder.reverseGeocodeLocation(location)
    {
        let output = placemarks.map({ mark in
            var output = ""
            output += (mark.areasOfInterest ?? []).joined(separator: " | ") + "\n"
            output += (mark.subLocality ?? "") + "\n"
            output += (mark.locality ?? "") + "\n"
            output += (mark.subAdministrativeArea ?? "") + "\n"
            output += (mark.administrativeArea ?? "") + "\n"
            output += (mark.country ?? "") + "\n"
            return output
        }).joined(separator: "\n\n")

        return output
    } else {
        return "Error"
    }
}

func mapkitLookup() async -> String {
    let request = MKReverseGeocodingRequest(location: location)
    let items = (try? await request?.mapItems) ?? []
    let output = items.map({ item in
        var output = ""
        output += "\(item.pointOfInterestCategory?.rawValue ?? "NA"): \(item.name ?? "No Name")" + "\n"
        output += (item.address?.fullAddress ?? "") + "\n"
        output += (item.address?.shortAddress ?? "") + "\n"
        output += (item.addressRepresentations?.cityName ?? "") + "\n"
        output += (item.addressRepresentations?.regionName ?? "") + "\n"
        output += (item.addressRepresentations?.cityWithContext(.full) ?? "") + "\n"
        output += (item.addressRepresentations?.cityWithContext(.short) ?? "") + "\n"
        output += (item.addressRepresentations?.cityWithContext(.automatic) ?? "") + "\n"
        output += (item.addressRepresentations?.fullAddress(includingRegion: true, singleLine: true) ?? "") + "\n"
        return output
    }).joined(separator: "\n\n")
    return output
}

Here are the results, side by side:

  CLGeocoder MapKit
Point of Interest a) Cypress Mountain
b) Cypress Provincial Park
Cypress Mountain
Neighbourhood Cypress Park 🫥 N/A
City ✅ West Vancouver ❌ Bowen Island
Area ✅ Metro Vancouver 🫥 N/A
Province ✅ BC ⚠️ (city), BC
Country ✅ Canada ✅ Canada
Postal Code ❌ V0N 1G2 ❌ V0N 1G2

CLGeocoder got it right on every level: Cypress Mountain (also matched as Cypress Provincial Park), in West Vancouver, in Metro Vancouver, in BC, Canada.

MapKit named the point of interest correctly, too — it knew this was Cypress Mountain. But every level above that was wrong. It placed the address, the city, and the region under Bowen Island — a separate island across Howe Sound, with no relationship to the mountain behind West Vancouver whatsoever.

Just for the reference, here are Bowen Island and West Vancouver on the same map:

The municipality of Bowen Island (the largest island) on the left, and the city of West Vancouver (the mainland) on the right.
The municipality of Bowen Island (the largest island) on the left, and the city of West Vancouver (the mainland) on the right.

That’s not a formatting difference or a naming preference between two reasonable answers. It’s just wrong.

It’s not just about correctness

Beyond correctness, MapKit’s AddressRepresentations also don’t get me clean information that I can work with:

So the MapKit replacement is not only inaccurate, it contains less information and is harder to work with.

A replacement that (still) isn’t ready

I don’t doubt Apple will get MapKit’s reverse geocoding sorted out eventually — it clearly has all the right data, since it named the point of interest correctly. But “eventually” isn’t “now,” and CLGeocoder having a deprecation warning on it doesn’t change that its output is the one you can trust today. Until MapKit stops making mistakes like this, I’m not switching, warning or not.

  1. CLGeocoder’s documentation has a coy deprecation message of two words: “Use MapKit”. There’s no link to the docs of MapKit, and there is no example. The deprecation message is not even a complete sentence with a period. My cynical read is that whoever wrote this two-word deprecation message was too embarrassed to even write up a full sentence, or to furnish the deprecation with an example.