关于 geotoolbox
面向 Swift 开发者的 GeoToolbox 技能,用于表示地点并解析地址。内容涵盖 PlaceDescriptor、MapKit 转换、异步地理编码,以及跨地点服务互操作所需的标识符。
主要功能
- 01用坐标或地址表示地点
- 02在 PlaceDescriptor 与 MKMapItem 间转换
- 03执行异步正向和反向地理编码
- 04保存多个地点服务的标识符
- 05安全处理可选地点属性
适用场景
- 01同时用地址和坐标表示地点
- 02将地址解析为地图项目
- 03根据坐标查找地址
- 04在系统间传递服务标识符
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill geotoolbox安装后在 Claude Code 中输入/geotoolbox确认已加载。
详细介绍
GeoToolbox 和 PlaceDescriptor 模式
使用 GeoToolbox 框架中的 PlaceDescriptor 表示可移植的位置。涵盖从坐标、地址和 MapKit 条目构建地点;使用新版异步 API 进行正向和反向地理编码;以及用于跨平台互操作的多服务地点标识符。
此技能的触发条件
在以下情况下使用此技能:
- 用户询问 GeoToolbox 或 PlaceDescriptor
- 用户想要用坐标、地址或常用名称表示地点
- 用户需要在 MKMapItem 和 PlaceDescriptor 之间进行转换
- 用户询问 正向地理编码(地址转坐标)或 反向地理编码(坐标转地址)
- 用户想要存储或传输多服务地点标识符(Apple Maps、Google Maps 等)
- 用户提到 PlaceRepresentation、SupportingPlaceRepresentation 或 MKGeocodingRequest
- 用户询问可移植或可互操作的位置数据结构
决策树
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 可用性
| API | 最低 OS 版本 | 导入 |
|---|---|---|
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 |
快速入门
从坐标创建 PlaceDescriptor
import 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")
从地址创建 PlaceDescriptor
import GeoToolbox
let descriptor = PlaceDescriptor(
representations: [.address("One Apple Park Way, Cupertino, CA 95014")],
commonName: "Apple Park"
)
if let address = descriptor.address {
print("Address: \(address)")
}
多种表示形式和服务标识符
import 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)")
}
从 MKMapItem 转换
import GeoToolbox
import MapKit
func descriptorFromMapItem(_ mapItem: MKMapItem) -> PlaceDescriptor {
PlaceDescriptor(item: mapItem)
}
正向地理编码(地址转坐标)
import 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)")
}
反向地理编码(坐标转地址)
import 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")")
}
完整集成示例
对地址进行地理编码,将结果转换为带服务标识符的 PlaceDescriptor,并读取所有属性:
import 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)")
}
}
常见错误
| # | 错误 | 问题 | 修复方法 |
|---|---|---|---|
| 1 | 使用 PlaceDescriptor 时只导入 MapKit | PlaceDescriptor 属于 GeoToolbox,而非 MapKit | 在 import MapKit 旁添加 import GeoToolbox |
| 2 | 硬编码服务标识符键 | 服务标识符键是字符串;拼写错误会导致静默失败 | 为 "com.apple.maps" 等服务键定义常量 |
| 3 | 未处理地理编码错误 | 网络或服务故障会抛出错误 | 使用 do/catch,或通过 try await 进行适当的错误处理 |
模式
服务标识符常量
定义常量,避免服务标识符键出现拼写错误:
// 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
}
// Bad -- raw string literals are error-prone
if let id = descriptor.serviceIdentifier(for: "com.apple.map") { // typo: "map" not "maps"
// silently nil
}
安全处理 nil 属性
// 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: " -- ")
}
// Bad -- force-unwrapping optional properties
let name = descriptor.commonName! // crashes if nil
let coord = descriptor.coordinate! // crashes if no coordinate representation
使用备用方案进行地理编码
// 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
}
}
// 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
}
检查清单
- 使用
PlaceDescriptor、PlaceRepresentation或SupportingPlaceRepresentation时,已导入GeoToolbox - 使用
MKGeocodingRequest、MKReverseGeocodingRequest或MKMapItem时,已导入MapKit - 使用
CLLocationCoordinate2D或CLLocation时,已导入CoreLocation - 使用前检查
PlaceDescriptor的属性(coordinate、address、commonName)是否为nil - 服务标识符键使用已定义的常量,而非原始字符串字面量
-
MKReverseGeocodingRequest接收的是CLLocation,而非原始CLLocationCoordinate2D - 访问地理编码结果中的元素前,先检查数组是否为空
- 地理编码调用使用
do/catch进行适当的async/await错误处理 - 已知坐标和地址时,同时提供多种表示形式
- 需要跨服务互操作时,附带服务标识符的辅助表示形式
交叉引用
- 有关 MapKit 地图视图和标注,请参阅 MapKit 文档
- 有关 CoreLocation 权限和位置更新,请参阅 CoreLocation 文档
- 有关地图的 SwiftUI 集成,请参阅 SwiftUI 中的
Map视图
参考资料
评论
支持 Markdown