Skip to content
intermediate Phase 7 · Advanced UI

MapKit & Core Location

Display maps, annotate locations, handle user location, and implement geofencing.

50m
3 problems
Topic Progress 0%

Map Display

The Map Component

SwiftUI provides a Map component that integrates with MapKit to display interactive maps. You can configure the map position, camera, and appearance using declarative syntax.

import SwiftUI
import MapKit

struct MapDemoView: View {
    @State private var position: MapCameraPosition = .region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        )
    )
    
    var body: some View {
        Map(position: $position) { }
        .mapControls {
            MapUserLocationButton()
            MapCompass()
            MapScaleView()
            MapPitchToggle()
        }
    }
}

Configuring Map Position

The map position uses MapCameraPosition to control what region is visible. You can set a fixed region, a 3D camera, or follow the user.

let region = MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
    span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)

Map(position: .constant(.region(region))) { }

// 3D camera view
Map(position: .constant(.camera(
    MapCamera(
        centerCoordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
        distance: 1000, heading: 45, pitch: 60
    )
))) { }

Map Appearance

Customize the map visual style using the mapStyle modifier with .standard, .imagery, or .hybrid options.

Map(position: $position) { }
    .mapStyle(.hybrid(elevation: .realistic))

Interactive Maps

Handle user interactions by responding to taps and toolbar actions to manipulate the map view programmatically.

struct InteractiveMapView: View {
    @State private var position: MapCameraPosition = .automatic
    @State private var selectedLocation: CLLocationCoordinate2D?
    
    var body: some View {
        Map(position: $position) {
            if let location = selectedLocation {
                Marker("Selected", systemImage: "mappin.circle.fill", coordinate: location)
                    .tint(.red)
            }
        }
        .toolbar {
            ToolbarItem(placement: .bottomBar) {
                Button("Reset") {
                    position = .region(MKCoordinateRegion(
                        center: CLLocationCoordinate2D(latitude: 0, longitude: 0),
                        span: MKCoordinateSpan(latitudeDelta: 180, longitudeDelta: 180)
                    ))
                }
            }
        }
    }
}

Annotations & Overlays

Adding Markers

Markers are the simplest way to annotate points on a map. They display a standard pin with a title at a specific coordinate.

struct AnnotatedMapView: View {
    @State private var position: MapCameraPosition = .region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
            span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
        )
    )
    
    let landmarks = [
        (name: "Golden Gate Bridge", lat: 37.8199, lon: -122.4783),
        (name: "Alcatraz Island", lat: 37.8267, lon: -122.4230),
        (name: "Fishermans Wharf", lat: 37.8080, lon: -122.4177)
    ]
    
    var body: some View {
        Map(position: $position) {
            ForEach(landmarks, id: \.name) { landmark in
                Marker(landmark.name, systemImage: "mappin.circle.fill",
                    coordinate: CLLocationCoordinate2D(latitude: landmark.lat, longitude: landmark.lon))
                    .tint(.red)
            }
        }
    }
}

Custom Annotations

Use Annotation to display custom SwiftUI views at map coordinates for full control over appearance.

Annotation("My Location", coordinate: myCoord) {
    VStack(spacing: 0) {
        Text("Home")
            .font(.caption)
            .padding(6)
            .background(.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        Image(systemName: "mappin.circle.fill")
            .font(.title)
            .foregroundColor(.blue)
    }
}

Map Overlays

Overlays draw shapes and routes on the map. SwiftUI provides MapPolyline, MapCircle, and MapPolygon.

let route = [
    CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
    CLLocationCoordinate2D(latitude: 37.8044, longitude: -122.2712),
    CLLocationCoordinate2D(latitude: 37.8199, longitude: -122.4783)
]

Map(position: $position) {
    MapPolyline(coordinates: route)
        .stroke(.blue, lineWidth: 4)
    MapCircle(center: route[0], radius: 500)
        .fill(.red.opacity(0.3))
    MapPolygon(coordinates: [
        CLLocationCoordinate2D(latitude: 37.78, longitude: -122.42),
        CLLocationCoordinate2D(latitude: 37.79, longitude: -122.41),
        CLLocationCoordinate2D(latitude: 37.78, longitude: -122.40)
    ]).fill(.green.opacity(0.3))
}

User Location

Requesting Location Permissions

Before accessing user location, you must request permission. Add NSLocationWhenInUseUsagePermission to your Info.plist.

import CoreLocation

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()
    @Published var userLocation: CLLocation?
    @Published var authStatus: CLAuthorizationStatus = .notDetermined
    
    override init() {
        super.init()
        manager.delegate = self
    }
    
    func requestPermission() {
        manager.requestWhenInUseAuthorization()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        userLocation = locations.last
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        authStatus = manager.authorizationStatus
    }
}

Displaying User Location

Combine LocationManager with Map to show the user position on the map.

struct UserLocationMapView: View {
    @StateObject private var locationManager = LocationManager()
    @State private var position: MapCameraPosition = .userLocation(fallback: .automatic)
    
    var body: some View {
        Map(position: $position) {
            UserAnnotation()
        }
        .mapControls { MapUserLocationButton() }
        .onAppear { locationManager.requestPermission() }
    }
}

Forward and Reverse Geocoding

MapKit includes geocoding services to convert between coordinates and human-readable addresses using MKLocalSearch and CLGeocoder.

func geocodeAddress(_ address: String) async throws -> MKMapItem {
    let request = MKLocalSearch.Request()
    request.naturalLanguageQuery = address
    let search = MKLocalSearch(request: request)
    let response = try await search.start()
    guard let item = response.mapItems.first else {
        throw NSError(domain: "GeocodingError", code: 0)
    }
    return item
}

func reverseGeocode(location: CLLocation) async throws -> String {
    let geocoder = CLGeocoder()
    let placemarks = try await geocoder.reverseGeocodeLocation(location)
    return placemarks.first?.name ?? "Unknown Location"
}

Quiz

1. What does MapCameraPosition control in SwiftUI maps?

Question 1 options

2. What Info.plist key is required for foreground location access?

Question 2 options

3. Which SwiftUI component draws a line between coordinates on a map?

Question 3 options

4. How do you display the user location on a Map in SwiftUI?

Question 4 options

Flashcards

Question

What is MapCameraPosition used for?

Answer

It controls the visible region, camera angle, heading, and pitch of a SwiftUI Map view.

Question

What is the difference between Marker and Annotation?

Answer

Marker provides a standard pin with title. Annotation allows custom view content at a coordinate.

Question

What overlay type draws a route on a map?

Answer

MapPolyline draws connected line segments between an array of coordinates.

Question

How do you request foreground location permission?

Answer

Call locationManager.requestWhenInUseAuthorization() and add NSLocationWhenInUseUsagePermission to Info.plist.

Revision Notes

Key Takeaways

  • 1. MapCameraPosition controls the visible region and camera
  • 2. Markers are simple pins; Annotations allow custom views
  • 3. MapPolyline, MapCircle, and MapPolygon draw overlays
  • 4. UserAnnotation() displays the user location
  • 5. Location permissions are required before accessing user location

Interview Tips

  • Explain the difference between MapCameraPosition and MKCoordinateRegion
  • Discuss when to use Annotation vs Marker
  • Describe how to handle location permission flow

Cheat Sheet

MapKit Quick Reference

  • Map(position:) - Main map view with camera position binding
  • MapCameraPosition - Controls visible region (.region, .camera, .userLocation)
  • Marker - Standard pin annotation
  • Annotation - Custom view annotation
  • MapPolyline/MapCircle/MapPolygon - Map overlays
  • UserAnnotation() - Displays user location
  • .mapStyle() - Set map visual style
  • .mapControls {} - Add standard map controls