axiom-mapkit-diag
3
总安装量
2
周安装量
#59696
全站排名
安装命令
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-mapkit-diag
Agent 安装分布
opencode
2
gemini-cli
2
codebuddy
2
github-copilot
2
codex
2
kimi-cli
2
Skill 文档
MapKit Diagnostics
Symptom-based MapKit troubleshooting. Start with the symptom you’re seeing, follow the diagnostic path.
Related Skills
axiom-mapkitâ Patterns, decision trees, anti-patternsaxiom-mapkit-refâ API reference, code examples
Quick Reference
| Symptom | Check First | Common Fix |
|---|---|---|
| Annotations not appearing | Coordinate values (lat/lng swapped?) | Verify coordinate, check viewFor delegate |
| Map region jumps/loops | updateUIView guard | Add region equality check |
| Slow with many annotations | Annotation count, view reuse | Enable clustering, implement view reuse |
| Clustering not working | clusteringIdentifier set? | Set same identifier on all views |
| Overlays not rendering | renderer delegate method | Return correct MKOverlayRenderer subclass |
| Search returns no results | resultTypes, region bias | Set appropriate resultTypes and region |
| User location not showing | Authorization status | Request CLLocationManager authorization first |
| Coordinates appear wrong | lat/lng order | MapKit uses (latitude, longitude) â verify data source |
Symptom 1: Annotations Not Appearing
Decision Tree
Q1: Are coordinates valid?
ââ 0,0 or NaN â Data source returning default/empty values
â Fix: Validate coordinates before adding annotations
â Debug: print("\(annotation.coordinate.latitude), \(annotation.coordinate.longitude)")
â
ââ Valid numbers â Check next
Q2: Are lat/lng swapped?
ââ YES (common with GeoJSON which uses [longitude, latitude]) â Swap values
â GeoJSON: [lng, lat] â MapKit: CLLocationCoordinate2D(latitude:, longitude:)
â Fix: CLLocationCoordinate2D(latitude: json[1], longitude: json[0])
â
ââ NO â Check next
Q3: (MKMapView) Is mapView(_:viewFor:) delegate returning nil for your annotations?
ââ Not implemented â System uses default pin (should appear)
ââ Returns nil â System uses default pin (should appear)
ââ Returns wrong view â Check implementation
â
ââ Check delegate is set
Q4: (MKMapView) Is delegate set?
ââ NO â mapView.delegate = self (or context.coordinator in UIViewRepresentable)
â Without delegate: default pins appear. But if viewFor returns nil, check annotation type
â
ââ YES â Check next
Q5: (SwiftUI) Are annotations in Map content builder?
ââ NO â Annotations must be inside Map { ... } content closure
â Fix: Map(position: $pos) { Marker("Name", coordinate: coord) }
â
ââ YES â Check next
Q6: Is the map region showing the annotation coordinates?
ââ Map centered elsewhere â Adjust camera/region to include annotation coordinates
â Debug: Compare mapView.region with annotation coordinates
â Fix: Use .automatic camera position or set region to fit annotations
â
ââ Region includes annotations â Check displayPriority
Q7: (MKMapView) Is displayPriority too low?
ââ .defaultLow â System may hide annotations at certain zoom levels
â Fix: view.displayPriority = .required for must-show annotations
â
ââ .required â Annotation should appear â file a bug report with minimal repro
Symptom 2: Map Region Jumping / Infinite Loops
Decision Tree
Q1: (UIViewRepresentable) Is setRegion called in updateUIView without guard?
ââ YES â Classic infinite loop:
â 1. SwiftUI state changes â updateUIView called
â 2. updateUIView calls setRegion
â 3. setRegion triggers regionDidChangeAnimated delegate
â 4. Delegate updates SwiftUI state â back to step 1
â
â Fix: Guard against unnecessary updates
â if mapView.region.center.latitude != region.center.latitude
â || mapView.region.center.longitude != region.center.longitude {
â mapView.setRegion(region, animated: true)
â }
â
â Alternative: Use a flag in coordinator
â coordinator.isUpdating = true
â mapView.setRegion(region, animated: true)
â coordinator.isUpdating = false
â // In regionDidChangeAnimated: guard !isUpdating
â
ââ NO â Check next
Q2: Are multiple state sources fighting over the region?
ââ YES â Two bindings or state variables controlling the same region
â Fix: Single source of truth for camera position
â One @State var cameraPosition, not two conflicting values
â
ââ NO â Check next
Q3: (SwiftUI) Is MapCameraPosition properly bound?
ââ Using .constant() or recreating position on each render â Camera resets
â Fix: @State private var cameraPosition: MapCameraPosition = .automatic
â Use the binding: Map(position: $cameraPosition)
â
ââ Properly bound â Check next
Q4: Animation conflict?
ââ Using animated: true in updateUIView alongside SwiftUI animations â Double animation
â Fix: Avoid animated: true in updateUIView, or disable SwiftUI animation for map
â
ââ NO â Check next
Q5: Is onMapCameraChange triggering state updates that move the camera?
ââ YES â Camera change â callback â state change â camera change
â Fix: Only update non-camera state in the callback
â Don't set cameraPosition inside onMapCameraChange
â
ââ NO â Check delegate implementation for unintended state mutations
Symptom 3: Performance Issues
Decision Tree
Q1: How many annotations?
ââ > 500 without clustering â Enable clustering
â SwiftUI: .mapItemClusteringIdentifier("poi")
â MKMapView: view.clusteringIdentifier = "poi"
â
ââ > 1000 â Consider visible-region filtering
â Only load annotations within mapView.region
â Use .onMapCameraChange to fetch when user scrolls
â
ââ < 500 â Check next
Q2: (MKMapView) Using dequeueReusableAnnotationView?
ââ NO â Every annotation creates a new view â memory spike
â Fix: Register view class and dequeue in delegate
â mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: "marker")
â
ââ YES â Check next
Q3: Complex custom annotation views?
ââ YES â Rich SwiftUI views or complex UIViews per annotation
â Fix: Pre-render to UIImage for MKAnnotationView.image
â Or simplify to MKMarkerAnnotationView with glyph
â
ââ NO â Check next
Q4: Overlays with many coordinates?
ââ YES â Polylines/polygons with 10K+ points
â Fix: Simplify geometry (Douglas-Peucker algorithm)
â Or render at reduced detail for zoomed-out views
â
ââ NO â Check next
Q5: Geocoding in a loop?
ââ YES â CLGeocoder has rate limit (~1/second)
â Fix: Batch geocoding, throttle requests, cache results
â Use MKLocalSearch for batch lookups instead of per-item geocoding
â
ââ NO â Profile with Instruments â Time Profiler for CPU, Allocations for memory
Symptom 4: Clustering Not Working
Decision Tree
Q1: Is clusteringIdentifier set on annotation views?
ââ NO â Clustering requires an identifier on each annotation view
â MKMapView: view.clusteringIdentifier = "poi" in viewFor delegate
â SwiftUI: .mapItemClusteringIdentifier("poi") on content
â
ââ YES â Check next
Q2: Are ALL relevant views using the SAME identifier?
ââ NO â Different identifiers = different cluster groups
â Fix: Use consistent identifier for annotations that should cluster together
â
ââ YES â Check next
Q3: (MKMapView) Is mapView(_:clusterAnnotationForMemberAnnotations:) needed?
ââ Not implemented â System creates default cluster
â If you need custom cluster appearance, implement this delegate method
â
ââ Implemented â Check return value
Q4: Too few annotations in visible area?
ââ YES â Clustering only activates when annotations physically overlap
â At low zoom (city level), 10 annotations might cluster
â At high zoom (street level), same 10 might all be visible individually
â
ââ NO â Check next
Q5: (MKMapView) Are annotation views registered?
ââ NO â Register both individual and cluster view classes
â mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: "marker")
â
ââ YES â Verify viewFor delegate handles both MKClusterAnnotation and individual annotations
Symptom 5: Overlays Not Rendering
Decision Tree
Q1: (MKMapView) Is mapView(_:rendererFor:) delegate method implemented?
ââ NO â Overlays require a renderer â without this delegate method, nothing renders
â Fix: Implement the delegate method, return appropriate renderer subclass
â
ââ YES â Check next
Q2: Is the correct renderer subclass returned?
ââ MKCircle â MKCircleRenderer
â MKPolyline â MKPolylineRenderer
â MKPolygon â MKPolygonRenderer
â MKTileOverlay â MKTileOverlayRenderer
â Mismatch â Crash or silent failure
â
ââ Correct â Check next
Q3: Is renderer styled?
ââ No strokeColor/fillColor/lineWidth set â Renderer exists but invisible
â Fix: Set at minimum strokeColor and lineWidth
â renderer.strokeColor = .systemBlue
â renderer.lineWidth = 2
â
ââ Styled â Check next
Q4: Overlay level wrong?
ââ .aboveRoads â Overlay may be behind labels (hard to see)
â Try: mapView.addOverlay(overlay, level: .aboveLabels)
â
ââ Check overlay coordinates match visible region
Q5: (SwiftUI) Using MapCircle/MapPolyline without styling?
ââ No .foregroundStyle or .stroke â May render transparent
â Fix: MapCircle(center: coord, radius: 500)
â .foregroundStyle(.blue.opacity(0.3))
â .stroke(.blue, lineWidth: 2)
â
ââ Styled â Check coordinates are within visible map region
Symptom 6: Search / Directions Failures
Decision Tree
Q1: Network available?
ââ NO â MapKit search requires network connectivity
â Fix: Check URLSession connectivity or NWPathMonitor
â
ââ YES â Check next
Q2: resultTypes too restrictive?
ââ Only .physicalFeature but searching for "Starbucks" â No results
â Fix: Use .pointOfInterest for businesses, .address for streets
â Or combine: [.pointOfInterest, .address]
â
ââ Appropriate â Check next
Q3: Region bias missing?
ââ NO region set â Results may be from anywhere in the world
â Fix: request.region = mapView.region (or visible region)
â This biases results to what the user can see
â
ââ Region set â Check next
Q4: Natural language query format?
ââ Structured format (lat/lng, codes) â Won't parse
â Good: "coffee shops near San Francisco"
â Good: "123 Main St"
â Bad: "lat:37.7 lng:-122.4 coffee"
â Bad: "POI_TYPE=cafe"
â
ââ Natural language â Check next
Q5: Rate limited?
ââ Getting errors after many requests â Apple rate-limits MapKit search
â Fix: Throttle searches, use MKLocalSearchCompleter for autocomplete
â Don't fire MKLocalSearch on every keystroke
â
ââ NO â Check next
Q6: (Directions) Source and destination valid?
ââ source or destination is nil â Request will fail
â Fix: Verify both are valid MKMapItem instances
â MKMapItem.forCurrentLocation() requires location authorization
â
ââ Both valid â Check transportType availability
Transit directions not available in all regions
Walking/driving available globally
Symptom 7: User Location Not Showing
Decision Tree
Q1: What is CLLocationManager.authorizationStatus?
ââ .notDetermined â Authorization never requested
â Fix: Request authorization first, then enable user location
â CLServiceSession(authorization: .whenInUse)
â
ââ .denied â User denied location access
â Fix: Show UI explaining value, link to Settings
â
ââ .restricted â Parental controls block access
â Fix: Inform user, cannot override
â
ââ .authorizedWhenInUse / .authorizedAlways â Check next
Q2: (MKMapView) Is showsUserLocation set to true?
ââ NO â mapView.showsUserLocation = true
â
ââ YES â Check next
Q3: (SwiftUI) Using UserAnnotation() in Map content?
ââ NO â Add UserAnnotation() inside Map { ... }
â
ââ YES â Check next
Q4: Running in Simulator?
ââ YES, no custom location set â Simulator doesn't have GPS
â Fix: Debug menu â Location â Custom Location (or Apple/City Bicycle Ride/etc.)
â Xcode: Debug â Simulate Location â pick a location
â
ââ Physical device â Check next
Q5: MapKit implicitly requests authorization â was it previously denied?
ââ MapKit shows no prompt if already denied
â Check: Settings â Privacy & Security â Location Services â Your App
â If "Never": User must manually re-enable
â
ââ Authorized â Check if location services enabled system-wide
Settings â Privacy & Security â Location Services â toggle at top
Q6: Location icon appearing but blue dot not on screen?
ââ User is outside the visible map region
â Fix: Use MapCameraPosition.userLocation(fallback: .automatic)
â Or add MapUserLocationButton() in .mapControls
â
ââ See axiom-core-location-diag for deeper location troubleshooting
Symptom 8: Coordinate System Confusion
Common coordinate mistakes that cause annotations to appear in wrong locations.
MapKit vs GeoJSON
| System | Order | Example |
|---|---|---|
| MapKit (CLLocationCoordinate2D) | latitude, longitude | CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42) |
| GeoJSON | longitude, latitude | [-122.42, 37.77] |
| Google Maps | latitude, longitude | Same as MapKit |
| PostGIS ST_MakePoint | longitude, latitude | Same as GeoJSON |
The #1 coordinate bug: Swapping lat/lng when parsing GeoJSON.
// â WRONG: Using GeoJSON order directly
let coord = CLLocationCoordinate2D(
latitude: geoJson[0], // This is longitude!
longitude: geoJson[1] // This is latitude!
)
// â
RIGHT: GeoJSON is [lng, lat], MapKit wants (lat, lng)
let coord = CLLocationCoordinate2D(
latitude: geoJson[1],
longitude: geoJson[0]
)
MKMapPoint vs CLLocationCoordinate2D
CLLocationCoordinate2Dâ geographic coordinates (lat/lng in degrees)MKMapPointâ projected coordinates for flat map rendering- Convert:
MKMapPoint(coordinate)andcoordinateproperty on MKMapPoint - Never use MKMapPoint x/y as lat/lng â they’re completely different number spaces
Validation
func isValidCoordinate(_ coord: CLLocationCoordinate2D) -> Bool {
coord.latitude >= -90 && coord.latitude <= 90
&& coord.longitude >= -180 && coord.longitude <= 180
&& !coord.latitude.isNaN && !coord.longitude.isNaN
}
If latitude > 90 or longitude > 180, coordinates are likely swapped or in wrong format.
Console Debugging
MapKit Logs
# View MapKit-related logs
log stream --predicate 'subsystem == "com.apple.MapKit"' --level debug
# Filter for your app
log stream --predicate 'process == "YourApp" AND (subsystem == "com.apple.MapKit" OR subsystem == "com.apple.CoreLocation")'
Common Console Messages
| Message | Meaning |
|---|---|
No renderer for overlay |
Missing rendererFor delegate method |
Reuse identifier not registered |
Call register before dequeue |
CLLocationManager authorizationStatus is denied |
User denied location |
Resources
WWDC: 2023-10043, 2024-10094
Docs: /mapkit, /mapkit/mklocalsearch
Skills: axiom-mapkit, axiom-mapkit-ref, axiom-core-location-diag