$npx -y skills add dpearson2699/swift-ios-skills --skill mapkitImplement, review, or improve maps and location features in iOS/macOS apps using MapKit and CoreLocation. Use when working with Map views, annotations, markers, polylines, user location tracking, geocoding, reverse geocoding, search/autocomplete, directions and routes, geofencing
| 1 | # MapKit |
| 2 | |
| 3 | Build map-based and location-aware features targeting iOS 17+ with SwiftUI |
| 4 | MapKit and modern CoreLocation async APIs. Use `Map` with `MapContentBuilder` |
| 5 | for views, `CLLocationUpdate.liveUpdates()` for streaming location, and |
| 6 | `CLMonitor` for geofencing. |
| 7 | |
| 8 | Read [references/mapkit-patterns.md](references/mapkit-patterns.md) when you need full map setup, search, |
| 9 | routes, Look Around, snapshots, or iOS 26 place APIs. Read |
| 10 | [references/mapkit-corelocation-patterns.md](references/mapkit-corelocation-patterns.md) when the task involves |
| 11 | location update lifecycle, geofencing, background location, testing, or privacy keys. |
| 12 | |
| 13 | ## Contents |
| 14 | |
| 15 | - [Workflow](#workflow) |
| 16 | - [SwiftUI Map View (iOS 17+)](#swiftui-map-view-ios-17) |
| 17 | - [CoreLocation Modern API](#corelocation-modern-api) |
| 18 | - [Geocoding](#geocoding) |
| 19 | - [Search](#search) |
| 20 | - [Directions](#directions) |
| 21 | - [PlaceDescriptor (iOS 26+)](#placedescriptor-ios-26) |
| 22 | - [Common Mistakes](#common-mistakes) |
| 23 | - [Review Checklist](#review-checklist) |
| 24 | - [References](#references) |
| 25 | |
| 26 | ## Workflow |
| 27 | |
| 28 | ### 1. Add a map with markers or annotations |
| 29 | |
| 30 | 1. Import `MapKit`. |
| 31 | 2. Create a `Map` view with optional `MapCameraPosition` binding. |
| 32 | 3. Add `Marker`, `Annotation`, `MapPolyline`, `MapPolygon`, or `MapCircle` |
| 33 | inside the `MapContentBuilder` closure. |
| 34 | 4. Configure map style with `.mapStyle()`. |
| 35 | 5. Add map controls with `.mapControls { }`. |
| 36 | 6. Handle selection with a `selection:` binding. |
| 37 | |
| 38 | ### 2. Track user location |
| 39 | |
| 40 | 1. Add `NSLocationWhenInUseUsageDescription` to Info.plist. |
| 41 | 2. On iOS 18+, create a `CLServiceSession` to manage authorization. |
| 42 | 3. Iterate `CLLocationUpdate.liveUpdates()` in a `Task`. |
| 43 | 4. Filter updates by distance or accuracy before updating the UI. |
| 44 | 5. Stop the task when location tracking is no longer needed. |
| 45 | |
| 46 | ### 3. Search for places |
| 47 | |
| 48 | 1. Configure `MKLocalSearchCompleter` for autocomplete suggestions. |
| 49 | 2. Debounce user input (at least 300ms) before setting the query. |
| 50 | 3. Convert selected completion to `MKLocalSearch.Request` for full results. |
| 51 | 4. Display results as markers or in a list. |
| 52 | |
| 53 | ### 4. Get directions and display a route |
| 54 | |
| 55 | 1. Create an `MKDirections.Request` with source and destination `MKMapItem`. |
| 56 | 2. Set `transportType` (`.automobile`, `.walking`, `.transit`, `.cycling`). |
| 57 | 3. Await `MKDirections.calculate()`. |
| 58 | 4. Draw the route with `MapPolyline(route.polyline)`. |
| 59 | |
| 60 | ### 5. Review existing map/location code |
| 61 | |
| 62 | Run through the Review Checklist at the end of this file. |
| 63 | |
| 64 | ## SwiftUI Map View (iOS 17+) |
| 65 | |
| 66 | ```swift |
| 67 | import MapKit |
| 68 | import SwiftUI |
| 69 | |
| 70 | struct PlaceMap: View { |
| 71 | @State private var position: MapCameraPosition = .automatic |
| 72 | |
| 73 | var body: some View { |
| 74 | Map(position: $position) { |
| 75 | Marker("Apple Park", coordinate: applePark) |
| 76 | Marker("Infinite Loop", systemImage: "building.2", |
| 77 | coordinate: infiniteLoop) |
| 78 | } |
| 79 | .mapStyle(.standard(elevation: .realistic)) |
| 80 | .mapControls { |
| 81 | MapUserLocationButton() |
| 82 | MapCompass() |
| 83 | MapScaleView() |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | ``` |
| 88 | |
| 89 | ### Marker and Annotation |
| 90 | |
| 91 | ```swift |
| 92 | // Balloon marker -- simplest way to pin a location |
| 93 | Marker("Cafe", systemImage: "cup.and.saucer.fill", coordinate: cafeCoord) |
| 94 | .tint(.brown) |
| 95 | |
| 96 | // Annotation -- custom SwiftUI view at a coordinate |
| 97 | Annotation("You", coordinate: userCoord, anchor: .bottom) { |
| 98 | Image(systemName: "figure.wave") |
| 99 | .padding(6) |
| 100 | .background(.blue.gradient, in: .circle) |
| 101 | .foregroundStyle(.white) |
| 102 | } |
| 103 | ``` |
| 104 | |
| 105 | ### Overlays: Polyline, Polygon, Circle |
| 106 | |
| 107 | ```swift |
| 108 | Map { |
| 109 | // Polyline from coordinates |
| 110 | MapPolyline(coordinates: routeCoords) |
| 111 | .stroke(.blue, lineWidth: 4) |
| 112 | |
| 113 | // Polygon (area highlight) |
| 114 | MapPolygon(coordinates: parkBoundary) |
| 115 | .foregroundStyle(.green.opacity(0.3)) |
| 116 | .stroke(.green, lineWidth: 2) |
| 117 | |
| 118 | // Circle (radius around a point) |
| 119 | MapCircle(center: storeCoord, radius: 500) |
| 120 | .foregroundStyle(.red.opacity(0.15)) |
| 121 | .stroke(.red, lineWidth: 1) |
| 122 | } |
| 123 | ``` |
| 124 | |
| 125 | ### Camera Position |
| 126 | |
| 127 | `MapCameraPosition` controls what the map displays. Bind it to let the user |
| 128 | interact and to programmatically move the camera. |
| 129 | |
| 130 | ```swift |
| 131 | // Center on a region |
| 132 | @State private var position: MapCameraPosition = .region( |
| 133 | MKCoordinateRegion( |
| 134 | center: CLLocationCoordinate2D(latitude: 37.334, longitude: -122.009), |
| 135 | span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) |
| 136 | ) |
| 137 | ) |
| 138 | |
| 139 | // Follow user location |
| 140 | @State private var position: MapCameraPosition = .userLocation( |