geotoolbox
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGeoToolbox and PlaceDescriptor Patterns
GeoToolbox 与 PlaceDescriptor 模式
Portable location representation using 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.
PlaceDescriptor使用GeoToolbox框架中的实现可移植的位置表示。涵盖从坐标、地址和MapKit项构建地点;使用新的异步API进行正向和反向地理编码;以及用于跨平台互操作的多服务地点标识符。
PlaceDescriptorWhen This Skill Activates
本技能适用场景
Use this skill when the user:
- Asks about GeoToolbox or PlaceDescriptor
- Wants to represent a place with coordinates, address, or common name
- Needs to convert between MKMapItem and PlaceDescriptor
- Asks about forward geocoding (address to coordinates) or reverse geocoding (coordinates to address)
- Wants to store or transmit multi-service place identifiers (Apple Maps, Google Maps, etc.)
- Mentions PlaceRepresentation, SupportingPlaceRepresentation, or MKGeocodingRequest
- Asks about portable or interoperable location data structures
当用户有以下需求时,可使用本技能:
- 询问GeoToolbox或PlaceDescriptor相关内容
- 希望用坐标、地址或通用名称表示某个地点
- 需要在MKMapItem和PlaceDescriptor之间进行转换
- 询问正向地理编码(地址转坐标)或反向地理编码(坐标转地址)相关操作
- 希望存储或传输多服务地点标识符(如Apple Maps、Google Maps等)
- 提及PlaceRepresentation、SupportingPlaceRepresentation或MKGeocodingRequest
- 询问可移植或可互操作的位置数据结构
Decision Tree
决策树
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")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 Availability
API 可用性
| API | Minimum OS | Import |
|---|---|---|
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 6 / macOS 10.9 | |
| iOS 2 / macOS 10.6 | |
| API | 最低系统版本 | 导入框架 |
|---|---|---|
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 26 / macOS 26 | |
| iOS 6 / macOS 10.9 | |
| iOS 2 / macOS 10.6 | |
Quick Start
快速入门
Create a PlaceDescriptor from Coordinates
从坐标创建PlaceDescriptor
swift
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")swift
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")Create a PlaceDescriptor from an Address
从地址创建PlaceDescriptor
swift
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)")
}swift
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)")
}Multiple Representations and Service Identifiers
多表示方式与服务标识符
swift
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)")
}swift
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)")
}Convert from MKMapItem
从MKMapItem转换
swift
import GeoToolbox
import MapKit
func descriptorFromMapItem(_ mapItem: MKMapItem) -> PlaceDescriptor {
PlaceDescriptor(item: mapItem)
}swift
import GeoToolbox
import MapKit
func descriptorFromMapItem(_ mapItem: MKMapItem) -> PlaceDescriptor {
PlaceDescriptor(item: mapItem)
}Forward Geocoding (Address to Coordinates)
正向地理编码(地址转坐标)
swift
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)")
}swift
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)")
}Reverse Geocoding (Coordinates to Address)
反向地理编码(坐标转地址)
swift
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")")
}swift
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")")
}Full Integration Example
完整集成示例
Geocode an address, convert the result to a PlaceDescriptor with service identifiers, and read back all properties:
swift
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)")
}
}对地址进行地理编码,将结果转换为带服务标识符的PlaceDescriptor,并读取所有属性:
swift
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)")
}
}Top Mistakes
常见错误
| # | Mistake | Problem | Fix |
|---|---|---|---|
| 1 | Importing only | | Add |
| 2 | Using | | Use |
| 3 | Assuming | A descriptor can be address-only with no coordinate | Check |
| 4 | Assuming | A descriptor can be coordinate-only with no address | Check |
| 5 | Hardcoding service identifier keys | Service identifier keys are strings; typos cause silent failures | Define constants for service keys like |
| 6 | Passing | The initializer takes a | Wrap in |
| 7 | Ignoring empty geocoding results | Geocoding can return zero results for ambiguous or invalid input | Guard against empty |
| 8 | Not handling geocoding errors | Network or service failures throw errors | Use |
| 序号 | 错误操作 | 问题 | 修复方案 |
|---|---|---|---|
| 1 | 使用 | | 在 |
| 2 | 使用 | | 使用 |
| 3 | 假设 | 描述符可能仅包含地址,没有坐标 | 使用前检查 |
| 4 | 假设 | 描述符可能仅包含坐标,没有地址 | 使用前检查 |
| 5 | 硬编码服务标识符键 | 服务标识符键为字符串,拼写错误会导致静默失败 | 为服务键(如 |
| 6 | 直接将 | 初始化器接受的是 | 先将坐标包装为 |
| 7 | 忽略空地理编码结果 | 对于模糊或无效输入,地理编码可能返回零结果 | 访问 |
| 8 | 未处理地理编码错误 | 网络或服务故障会抛出错误 | 使用 |
Patterns
最佳实践
Service Identifier Constants
服务标识符常量
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 -- 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
}Nil-Safe Property Access
空安全属性访问
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 representationswift
// 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 representationGeocoding with Fallback
带降级处理的地理编码
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
}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
}Review Checklist
检查清单
- is present when using
import GeoToolbox,PlaceDescriptor, orPlaceRepresentationSupportingPlaceRepresentation - is present when using
import MapKit,MKGeocodingRequest, orMKReverseGeocodingRequestMKMapItem - is present when using
import CoreLocationorCLLocationCoordinate2DCLLocation - properties (
PlaceDescriptor,coordinate,address) are checked forcommonNamebefore usenil - Service identifier keys use defined constants, not raw string literals
- receives a
MKReverseGeocodingRequest, not a rawCLLocationCLLocationCoordinate2D - Geocoding results are checked for empty arrays before accessing elements
- Geocoding calls use proper error handling with
async/awaitdo/catch - Multiple representations are provided when both coordinate and address are known
- Supporting representations include service identifiers when cross-service interop is needed
- 使用、
PlaceDescriptor或PlaceRepresentation时,已导入SupportingPlaceRepresentationGeoToolbox - 使用、
MKGeocodingRequest或MKReverseGeocodingRequest时,已导入MKMapItemMapKit - 使用或
CLLocationCoordinate2D时,已导入CLLocationCoreLocation - 使用的属性(
PlaceDescriptor、coordinate、address)前已检查是否为commonNamenil - 服务标识符键使用定义的常量,而非原始字符串字面量
- 接收的是
MKReverseGeocodingRequest,而非原始CLLocationCLLocationCoordinate2D - 访问地理编码结果前已检查数组是否为空
- 地理编码调用使用了正确的错误处理(
async/await)do/catch - 当同时知道坐标和地址时,提供了多种表示方式
- 需要跨服务互操作时,支持表示中包含服务标识符
Cross-References
交叉参考
- For MapKit map views and annotations, see MapKit documentation
- For CoreLocation permissions and location updates, see CoreLocation documentation
- For SwiftUI integration with maps, see view in SwiftUI
Map
- 关于MapKit地图视图和标注,请查看MapKit文档
- 关于CoreLocation权限和位置更新,请查看CoreLocation文档
- 关于地图的SwiftUI集成,请查看SwiftUI中的视图
Map