Map in SwiftUI (episode IV – Search Around)

In this episode, we’ll learn how to conduct a search on the map. Our objective is to achieve something like this:

Therefore, by typing in a text field what we want to search for, the points of interest found will be displayed on the map. In the example, I typed Shop, but you can type anything else.

The Map

Let’s start by looking at the view that contains the map:

import SwiftUI
import MapKit

struct ContentView: View {
    @State var position: MapCameraPosition = .userLocation(fallback: .automatic)
    @State private var searchResults = [SearchResult]()
    @State private var selectedLocation: SearchResult?
    @State private var searchText: String = ""
    
    var body: some View {
        VStack {
            TextField("Search", text: $searchText)
            .onSubmit {
                Task {
                    // searchResults = 
                }
            }.padding()
            Map(position: $position, selection: $selectedLocation) {
                ForEach(searchResults) { result in
                    Marker(coordinate: result.location) {
                        Image(systemName: "mappin")
                        Text(result.name)
                    }
                    .tag(result)
                }
            }
        }
    }
}
  • The position variable contains the user’s current location.
  • searchResults holds the results of the search.
  • selectedLocation stores the marker that has been selected (the item chosen from the search results).
  • searchText is the text input used for the query.

When the user submits the search string, the search function is called. The map centers on position (which, in this case, is the user’s location), and selectedLocation is updated when a marker is tapped.

Using ForEach, we add all the searched points of interest to the map.

The Search

Now, let’s take a look at the search logic.

First, let’s define the structure used in the search:

struct SearchResult: Identifiable, Hashable {
    let id = UUID()
    let location: CLLocationCoordinate2D
    let name: String
    
    static func == (lhs: SearchResult, rhs: SearchResult) -> Bool {
        lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

So, we implement the Identifiable protocol to have a unique identifier and the Hashable protocol to enable the use of this type of item as a selection on the Map.

Now, let’s focus on the core of the logic:

func search(with query: String, coordinate: CLLocationCoordinate2D? = nil) async throws -> [SearchResult] {
        let mapKitRequest = MKLocalSearch.Request()
        mapKitRequest.naturalLanguageQuery = query
        mapKitRequest.resultTypes = .pointOfInterest
        if let coordinate {
            mapKitRequest.region = .init(.init(origin: .init(coordinate), size: .init(width: 1, height: 1)))
        }
        let search = MKLocalSearch(request: mapKitRequest)

        let response = try await search.start()

        return response.mapItems.compactMap { mapItem in
            guard let location = mapItem.placemark.location?.coordinate else { return nil }

            return .init(location: location, name: mapItem.placemark.name ?? "")
        }
    }

This function receives the search string and coordinates for the search location as input (if ‘nil’, the current location is used), and it returns an array of SearchResult.

In the function, we create a request specifying that we use natural language and that our aim is to search for points of interest.

After that, we remove any points of interest with ‘nil’ coordinates and instead create SearchResult entity by adding coordinate and name.

That’s all.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts