geotoolbox

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GeoToolbox and PlaceDescriptor Patterns

GeoToolbox 与 PlaceDescriptor 模式

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.
使用GeoToolbox框架中的
PlaceDescriptor
实现可移植的位置表示。涵盖从坐标、地址和MapKit项构建地点;使用新的异步API进行正向和反向地理编码;以及用于跨平台互操作的多服务地点标识符。

When 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
当用户有以下需求时,可使用本技能:
  • 询问GeoToolboxPlaceDescriptor相关内容
  • 希望用坐标、地址或通用名称表示某个地点
  • 需要在MKMapItemPlaceDescriptor之间进行转换
  • 询问正向地理编码(地址转坐标)或反向地理编码(坐标转地址)相关操作
  • 希望存储或传输多服务地点标识符(如Apple Maps、Google Maps等)
  • 提及PlaceRepresentationSupportingPlaceRepresentationMKGeocodingRequest
  • 询问可移植或可互操作的位置数据结构

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 可用性

APIMinimum OSImport
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
API最低系统版本导入框架
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

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

常见错误

#MistakeProblemFix
1Importing only
MapKit
when using
PlaceDescriptor
PlaceDescriptor
lives in
GeoToolbox
, not
MapKit
Add
import GeoToolbox
alongside
import MapKit
2Using
CLGeocoder
instead of
MKGeocodingRequest
CLGeocoder
returns
CLPlacemark
which lacks MapKit integration
Use
MKGeocodingRequest
/
MKReverseGeocodingRequest
for
MKMapItem
results
3Assuming
PlaceDescriptor
always has a coordinate
A descriptor can be address-only with no coordinateCheck
descriptor.coordinate
for
nil
before use
4Assuming
PlaceDescriptor
always has an address
A descriptor can be coordinate-only with no addressCheck
descriptor.address
for
nil
before use
5Hardcoding service identifier keysService identifier keys are strings; typos cause silent failuresDefine constants for service keys like
"com.apple.maps"
6Passing
CLLocationCoordinate2D
directly to
MKReverseGeocodingRequest
The initializer takes a
CLLocation
, not a raw coordinate
Wrap in
CLLocation(latitude:longitude:)
first
7Ignoring empty geocoding resultsGeocoding can return zero results for ambiguous or invalid inputGuard against empty
mapItems
arrays
8Not handling geocoding errorsNetwork or service failures throw errorsUse
do/catch
or
try await
with proper error handling
序号错误操作问题修复方案
1使用
PlaceDescriptor
时仅导入
MapKit
PlaceDescriptor
属于
GeoToolbox
框架,而非
MapKit
import MapKit
之外添加
import GeoToolbox
2使用
CLGeocoder
而非
MKGeocodingRequest
CLGeocoder
返回的
CLPlacemark
缺乏MapKit集成
使用
MKGeocodingRequest
/
MKReverseGeocodingRequest
获取
MKMapItem
结果
3假设
PlaceDescriptor
始终包含坐标
描述符可能仅包含地址,没有坐标使用前检查
descriptor.coordinate
是否为
nil
4假设
PlaceDescriptor
始终包含地址
描述符可能仅包含坐标,没有地址使用前检查
descriptor.address
是否为
nil
5硬编码服务标识符键服务标识符键为字符串,拼写错误会导致静默失败为服务键(如
"com.apple.maps"
)定义常量
6直接将
CLLocationCoordinate2D
传入
MKReverseGeocodingRequest
初始化器接受的是
CLLocation
,而非原始坐标
先将坐标包装为
CLLocation(latitude:longitude:)
7忽略空地理编码结果对于模糊或无效输入,地理编码可能返回零结果访问
mapItems
数组前先判断是否为空
8未处理地理编码错误网络或服务故障会抛出错误使用
do/catch
try await
进行适当的错误处理

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 representation
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

Geocoding 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

检查清单

  • import GeoToolbox
    is present when using
    PlaceDescriptor
    ,
    PlaceRepresentation
    , or
    SupportingPlaceRepresentation
  • import MapKit
    is present when using
    MKGeocodingRequest
    ,
    MKReverseGeocodingRequest
    , or
    MKMapItem
  • import CoreLocation
    is present when using
    CLLocationCoordinate2D
    or
    CLLocation
  • PlaceDescriptor
    properties (
    coordinate
    ,
    address
    ,
    commonName
    ) are checked for
    nil
    before use
  • Service identifier keys use defined constants, not raw string literals
  • MKReverseGeocodingRequest
    receives a
    CLLocation
    , not a raw
    CLLocationCoordinate2D
  • Geocoding results are checked for empty arrays before accessing elements
  • Geocoding calls use proper
    async/await
    error handling with
    do/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
    SupportingPlaceRepresentation
    时,已导入
    GeoToolbox
  • 使用
    MKGeocodingRequest
    MKReverseGeocodingRequest
    MKMapItem
    时,已导入
    MapKit
  • 使用
    CLLocationCoordinate2D
    CLLocation
    时,已导入
    CoreLocation
  • 使用
    PlaceDescriptor
    的属性(
    coordinate
    address
    commonName
    )前已检查是否为
    nil
  • 服务标识符键使用定义的常量,而非原始字符串字面量
  • MKReverseGeocodingRequest
    接收的是
    CLLocation
    ,而非原始
    CLLocationCoordinate2D
  • 访问地理编码结果前已检查数组是否为空
  • 地理编码调用使用了正确的
    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
    Map
    view in SwiftUI
  • 关于MapKit地图视图和标注,请查看MapKit文档
  • 关于CoreLocation权限和位置更新,请查看CoreLocation文档
  • 关于地图的SwiftUI集成,请查看SwiftUI中的
    Map
    视图

References

参考链接