swiftuiios26

Reverse Geocoding: Getting MKAddress or MKAddressRepresentation from MKMapItem


I need some help with the new reverse geocoding operation in iOS 26. Apple has some sample code under the topic MKReverseGeocodingRequest. I have been trying to fill in the missing pieces to display the actual location on the screen.

As I understand it, MKReverseGeocodingRequest returns with an array of MKMapItem. But I can't get MKMapItem in a format for use with MKAddress (fullAddress or shortAddress) or MKAddressRepresentation (cityName, region, regionName, cityWithContext) to display within a view. I find it interesting that I can get the location to display with .name or .description, while neither are part of MKAddress or MKAddressRepresentation (Xcode AI suggested these).

Questions: With MKMapItem, how do I display the address with MKAddress or MKAddressRepresentation? Are there other address display options?

struct MyReverseGeocoderView: View {

 let fountainCoordinates = [
        CLLocation(latitude: 39.042617, longitude: -94.587526),
        CLLocation(latitude: 40.774313, longitude: -73.970835),
        CLLocation(latitude: -33.870986, longitude: 151.211786),
        CLLocation(latitude: 41.875790, longitude: -87.618953),
    ]

          @State var fountains: [MKMapItem]?
   
    var body: some View {
      
        VStack {
            if let fountains {

               ForEach(fountains, id: \.name) { item in
                    Text(item.description)
                    Text(item.name ?? "")
                    
                }
               } 
            }
        }
        .task {
            var fountainMapItems = [MKMapItem]()
            for coordinate in fountainCoordinates {
                if let request = MKReverseGeocodingRequest(location: coordinate) {
                    let mapitems = try? await request.mapItems
                    if let mapitem = mapitems?.first {
                        fountainMapItems.append(mapitem)
                    }
                }
            }
            fountains = fountainMapItems
            // The fountains `MKMapItems` array contains information describing
            // details about the following places based on the provided coordinates:
            //
            // Mill Creek Park Fountain, Kansas City, Missouri
            // Bethesda Terrace Fountain, Central Park, New York City
            // Archibald Fountain, Sydney, Australia
            // Buckingham Fountain, Chicago, Illinois
        }
    }
}


Solution

  • You could try this approach using MKMapItem address instance property, to display the fullAddress or shortAddress.

    Similarly using addressRepresentations.

    Example code:

    struct ContentView: View {
        var body: some View {
            MyReverseGeocoderView()
        }
    }
    
    struct MyReverseGeocoderView: View {
        
        let fountainCoordinates = [
            CLLocation(latitude: 39.042617, longitude: -94.587526),
            CLLocation(latitude: 40.774313, longitude: -73.970835),
            CLLocation(latitude: -33.870986, longitude: 151.211786),
            CLLocation(latitude: 41.875790, longitude: -87.618953),
        ]
        
        @State private var fountains: [MKMapItem] = []
        
        var body: some View {
            VStack {
                ForEach(fountains, id: \.name) { item in
                    // Text(item.description)
                    // Text(item.name ?? "")
                    
                    Text(item.address?.fullAddress ?? "no data").foregroundStyle(.blue)
                    Text(item.address?.shortAddress ?? "no data").foregroundStyle(.red)
                }
            }
            .task {
                for coordinate in fountainCoordinates {
                    if let request = MKReverseGeocodingRequest(location: coordinate) {
                        do {
                            let mapitems = try await request.mapItems
                            if let mapitem = mapitems.first {
                                fountains.append(mapitem)
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
        }
    }
    

    Note, for a more robust implementation use a unique identifier for the fountains elements (required for the ForEach). For example:

    
    struct MyMapItem: Identifiable {
        let id = UUID()
        let mapItem: MKMapItem
    }
    
    struct MyReverseGeocoderView: View {
        
        let fountainCoordinates = [
            CLLocation(latitude: 39.042617, longitude: -94.587526),
            CLLocation(latitude: 40.774313, longitude: -73.970835),
            CLLocation(latitude: -33.870986, longitude: 151.211786),
            CLLocation(latitude: 41.875790, longitude: -87.618953),
        ]
        
        @State private var fountains: [MyMapItem] = []
    
        var body: some View {
            VStack {
                ForEach(fountains) { item in
                    Text(item.mapItem.address?.fullAddress ?? "no data").foregroundStyle(.blue)
                    Text(item.mapItem.address?.shortAddress ?? "no data").foregroundStyle(.red)
                }
            }
            .task {
                for coordinate in fountainCoordinates {
                    if let request = MKReverseGeocodingRequest(location: coordinate) {
                        do {
                            let mapitems = try await request.mapItems
                            if let mapitem = mapitems.first {
                                fountains.append(MyMapItem(mapItem: mapitem))
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
        }
    }