Install any skill in seconds. Free to start, no credit card required.
Get Started Free →GeoToolbox PlaceDescriptor patterns with MapKit integration for location representation, geocoding, and multi-service place identifiers. Use when working with place descriptors, geocoding, or cross-service location data.
.claude/skills/rshankras-geotoolbox/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 53% | 0% |
Portable location representation using PlaceDescriptor from the GeoToolbox framework. Covers place construction from coordinates, addresses, and MapKit items; forward and reverse geocoding with the new async APIs; and multi-service place identifiers for cross-platform interop.
Use this skill when the user:
What do you need?
|
+-- Represent a place with coordinates and/or address
| +-- From a known coordinate
| | +-- PlaceRepresentation.coordinate(CLLocationCoordinate2D)
| +-- From a known address string
| | +-- PlaceRepresentation.address(String)
| +-- Both coordinate and address
| | +-- Pass multiple representations to PlaceDescriptor
| +-- From an existing MKMapItem
| +-- PlaceDescriptor(item: MKMapItem)
|
+-- Geocode an address to coordinates
| +-- MKGeocodingRequest(addressString:)
| +-- try await request.mapItems
|
+-- Reverse geocode coordinates to an address
| +-- MKReverseGeocodingRequest(location:)
| +-- try await request.mapItems
|
+-- Attach service identifiers (Apple Maps, Google, etc.)
| +-- SupportingPlaceRepresentation.serviceIdentifiers([String: String])
|
+-- Read place properties
+-- descriptor.coordinate, descriptor.address, descriptor.commonName
+-- descriptor.serviceIdentifier(for: "com.apple.maps")| API | Minimum OS | Import | |-----|-----------|--------| | PlaceDescriptor | iOS 26 / macOS 26 | GeoToolbox | | PlaceRepresentation | iOS 26 / macOS 26 | GeoToolbox | | SupportingPlaceRepresentation | iOS 26 / macOS 26 | GeoToolbox | | MKGeocodingRequest | iOS 26 / macOS 26 | MapKit | | MKReverseGeocodingRequest | iOS 26 / macOS 26 | MapKit | | PlaceDescriptor(item:) | iOS 26 / macOS 26 | GeoToolbox + MapKit | | MKMapItem | iOS 6 / macOS 10.9 | MapKit | | CLLocationCoordinate2D | iOS 2 / macOS 10.6 | CoreLocation |
swiftimport GeoToolbox import CoreLocation let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090) let descriptor = PlaceDescriptor( representations: [.coordinate(coordinate)], commonName: "Apple Park" ) // Read back if let coord = descriptor.coordinate { print("Lat: \(coord.latitude), Lon: \(coord.longitude)") } print(descriptor.commonName ?? "No name")
swiftimport GeoToolbox let descriptor = PlaceDescriptor( representations: [.address("One Apple Park Way, Cupertino, CA 95014")], commonName: "Apple Park" ) if let address = descriptor.address { print("Address: \(address)") }
swiftimport GeoToolbox import CoreLocation let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090) let descriptor = PlaceDescriptor( representations: [ .coordinate(coordinate), .address("One Apple Park Way, Cupertino, CA 95014") ], commonName: "Apple Park", supportingRepresentations: [ .serviceIdentifiers([ "com.apple.maps": "apple-maps-id-12345", "com.google.maps": "ChIJ-bfVTh8_j4ARDMPaL2Njo3I" ]) ] ) // Access a specific service identifier if let appleId = descriptor.serviceIdentifier(for: "com.apple.maps") { print("Apple Maps ID: \(appleId)") }
swiftimport GeoToolbox import MapKit func descriptorFromMapItem(_ mapItem: MKMapItem) -> PlaceDescriptor { PlaceDescriptor(item: mapItem) }
swiftimport MapKit func geocodeAddress(_ addressString: String) async throws -> [MKMapItem] { let request = MKGeocodingRequest(addressString: addressString) let mapItems = try await request.mapItems return mapItems } // Usage let items = try await geocodeAddress("One Apple Park Way, Cupertino, CA") if let first = items.first { let coord = first.placemark.coordinate print("Found: \(coord.latitude), \(coord.longitude)") }
swiftimport MapKit import CoreLocation func reverseGeocode(_ coordinate: CLLocationCoordinate2D) async throws -> [MKMapItem] { let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) let request = MKReverseGeocodingRequest(location: location) let mapItems = try await request.mapItems return mapItems } // Usage let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090) let items = try await reverseGeocode(coordinate) if let first = items.first { print("Address: \(first.placemark.title ?? "Unknown")") }
Geocode an address, convert the result to a PlaceDescriptor with service identifiers, and read back all properties:
swiftimport GeoToolbox import MapKit import CoreLocation func buildPlaceDescriptor(from addressString: String) async throws -> PlaceDescriptor? { // Forward geocode let request = MKGeocodingRequest(addressString: addressString) let mapItems = try await request.mapItems guard let mapItem = mapItems.first else { return nil } // Convert MKMapItem to PlaceDescriptor var descriptor = PlaceDescriptor(item: mapItem) // Or build manually with extra data let coordinate = mapItem.placemark.coordinate descriptor = PlaceDescriptor( representations: [ .coordinate(coordinate), .address(addressString) ], commonName: mapItem.name, supportingRepresentations: [ .serviceIdentifiers([ "com.apple.maps": "resolved-id-\(coordinate.latitude)" ]) ] ) return descriptor } func displayDescriptor(_ descriptor: PlaceDescriptor) { if let name = descriptor.commonName { print("Name: \(name)") } if let coord = descriptor.coordinate { print("Coordinate: \(coord.latitude), \(coord.longitude)") } if let address = descriptor.address { print("Address: \(address)") } if let appleId = descriptor.serviceIdentifier(for: "com.apple.maps") { print("Apple Maps ID: \(appleId)") } }
| # | Mistake | Problem | Fix | |---|---------|---------|-----| | 1 | Importing only MapKit when using PlaceDescriptor | PlaceDescriptor lives in GeoToolbox, not MapKit | Add import GeoToolbox alongside import MapKit | | 2 | Hardcoding service identifier keys | Service identifier keys are strings; typos cause silent failures | Define constants for service keys like "com.apple.maps" | | 3 | Not handling geocoding errors | Network or service failures throw errors | Use do/catch or try await with proper error handling |
Define constants to avoid typos in service identifier keys:
swift// Good -- constants prevent typos enum PlaceService { static let appleMaps = "com.apple.maps" static let googleMaps = "com.google.maps" static let foursquare = "com.foursquare" } if let id = descriptor.serviceIdentifier(for: PlaceService.appleMaps) { // use id }
swift// Bad -- raw string literals are error-prone if let id = descriptor.serviceIdentifier(for: "com.apple.map") { // typo: "map" not "maps" // silently nil }
swift// Good -- check each optional property func formatPlace(_ descriptor: PlaceDescriptor) -> String { var parts: [String] = [] if let name = descriptor.commonName { parts.append(name) } if let address = descriptor.address { parts.append(address) } if let coord = descriptor.coordinate { parts.append("\(coord.latitude), \(coord.longitude)") } return parts.joined(separator: " -- ") }
swift// Bad -- force-unwrapping optional properties let name = descriptor.commonName! // crashes if nil let coord = descriptor.coordinate! // crashes if no coordinate representation
swift// Good -- handle empty results and errors func resolvePlace(_ address: String) async -> PlaceDescriptor? { do { let request = MKGeocodingRequest(addressString: address) let items = try await request.mapItems guard let item = items.first else { print("No results for address: \(address)") return nil } return PlaceDescriptor(item: item) } catch { print("Geocoding failed: \(error.localizedDescription)") return nil } }
swift// Bad -- no error handling, no empty check func resolvePlace(_ address: String) async -> PlaceDescriptor { let request = MKGeocodingRequest(addressString: address) let items = try! await request.mapItems // crashes on failure return PlaceDescriptor(item: items.first!) // crashes if empty }
import GeoToolbox is present when using PlaceDescriptor, PlaceRepresentation, or SupportingPlaceRepresentationimport MapKit is present when using MKGeocodingRequest, MKReverseGeocodingRequest, or MKMapItemimport CoreLocation is present when using CLLocationCoordinate2D or CLLocationPlaceDescriptor properties (coordinate, address, commonName) are checked for nil before useMKReverseGeocodingRequest receives a CLLocation, not a raw CLLocationCoordinate2Dasync/await error handling with do/catchMap view in SwiftUIOther measured skills in the registry, with their headline benchmark lift.