Part- 3: My Journey with Map Search in the Community App

Adding Donation Pickup Points with Maps

When I was building the Donation Pickup Point feature in my first community app, I knew I needed to integrate a map. The idea was simple:

  • A donor should be able to choose a pickup location.

  • Other users should see it on the map easily.

So I turned to MapKit, Apple’s native framework for maps. It worked beautifully for showing maps and pins. But when it came to searching places, I quickly hit a wall:

  • MapKit’s search results were poor and limited.

  • Many local areas (which were important for donation pickup points) simply didn’t appear.

This was frustrating because the app relied heavily on accurate search.

At first, I thought about Google Places API, it’s reliable and accurate worldwide. But it came with a problem: it’s expensive, and for an early-stage experimental project, I couldn’t afford that.


Nominatim API as Alternative

This was also my first work with Map integration in an app, and I quickly realized MapKit’s built-in search wasn’t enough. The results were too limited, and many local areas (needed for donation pickup points) were missing.

That’s when I found Nominatim Search. It’s free, open-source, and much better for covering smaller/local places.

Here’s how I used it:

  • MapKit → for rendering maps and dropping pins.

  • Nominatim API → for handling search queries and returning better location results.

  • A custom list → to show search results, from which users could select their pickup point.


Introducing Nominatim API & NominatimPlace

  • What it is: A free, open-source geocoding/search API powered by OpenStreetMap.

  • What it does: Turns a text query like "Rangpur Medical College" into latitude & longitude, or finds places nearby.

  • Why it’s great: Free, covers smaller/local areas, perfect for MVPs or experiments.

To use it in Swift, we create a model to hold the results called NominatimPlace. This is just a custom struct that matches the JSON returned by Nominatim:

struct NominatimPlace : Decodable, Hashable {
    let place_id: Int
    let display_name: String
    let lat: String
    let lon: String

    var coordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(latitude: Double(lat) ?? 0, longitude: Double(lon) ?? 0)
    }
}

Breaking it down:

  • place_id → unique ID for the place from Nominatim.

  • display_name → human-readable name of the place (e.g., “Dhaka, Bangladesh”).

  • lat & lon → latitude and longitude as strings (returned by the API).

  • coordinate → converts lat and lon into CLLocationCoordinate2D so MapKit can use it to drop pins.

Searching Places with Nominatim API in Swift

This function handles searching places using Nominatim API and returns a list of NominatimPlace objects that you can use in your app:

func nominatimSearch(query: String) async throws -> [NominatimPlace] {
    // Prepare URL with query and JSON format
    let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" //prepares a user’s search input
    let urlString = "https://nominatim.openstreetmap.org/search?q=\(escapedQuery)&format=json&addressdetails=1&limit=10" //return search result in JSON

    guard let url = URL(string: urlString) else {
        return []
    }

    // Create request with custom User-Agent as required by Nominatim's usage policy
    var request = URLRequest(url: url)
    request.setValue("MapMonitoring - nusratjahan6169@gmail.com", forHTTPHeaderField: "User-Agent")

    let (data, _) = try await URLSession.shared.data(for: request)
    let places = try JSONDecoder().decode([NominatimPlace].self, from: data)
    return places
}

Step-by-step Explanation:

  1. Escape the query:
    addingPercentEncoding ensures that spaces or special characters in the user’s search input are safe for URLs.

  2. Build the URL:
    We create the Nominatim search URL with the query, asking for JSON format, address details, and limiting results to 10.

  3. Check URL validity:
    If URL creation fails, the function safely returns an empty list.

  4. Add User-Agent header:
    Nominatim requires a custom User-Agent in the request. This identifies your app to the service.

  5. Fetch data asynchronously:
    Using URLSession.shared.data(for:) with async/await calls the API and waits for the response.

  6. Decode JSON into Swift objects:
    JSONDecoder converts the returned JSON into [NominatimPlace], which you can now use in SwiftUI or MapKit.


Conclusion – My First Map Integration Journey

As someone new to Map integration, this was my first real attempt at connecting search, maps, and user interaction in an app. Along the way, I stumbled over:

  • Limited search results in MapKit

  • Figuring out a way to use free alternatives instead of expensive APIs

  • Converting API responses into something SwiftUI and MapKit can understand

Using Nominatim API taught me that for MVPs or experimental projects, free and open-source tools can work really well, even if they aren’t perfect.

A note to the community: If you’ve tried a better or smarter free approach for location search and map integration, I’d love to hear it . Sharing tips helps all of us learn faster!


0
Subscribe to my newsletter

Read articles from Tabassum Akter Nusrat directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Tabassum Akter Nusrat
Tabassum Akter Nusrat

Former Android dev at Samsung R&D, now diving deep into iOS development. Writing daily about Swift Concurrency, SwiftUI, and the journey of building real-world apps. Passionate about clean architecture, mental health tech, and learning in public.