geotoolbox について
Swift で場所を表現し、住所を解決する開発者向けの GeoToolbox スキルです。PlaceDescriptor、MapKit との変換、非同期ジオコーディング、複数の場所サービス間で使う識別子を扱います。
主な機能
- 01座標や住所で場所を表現
- 02PlaceDescriptor と 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 は MapKit ではなく GeoToolbox にあります | 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を使う場合、import GeoToolboxが含まれている -
MKGeocodingRequest、MKReverseGeocodingRequest、またはMKMapItemを使う場合、import MapKitが含まれている -
CLLocationCoordinate2DまたはCLLocationを使う場合、import CoreLocationが含まれている -
PlaceDescriptorのプロパティ(coordinate、address、commonName)を使用する前にnilかどうかを確認している - サービス識別子のキーには、生の文字列リテラルではなく定義済みの定数を使用している
-
MKReverseGeocodingRequestに生のCLLocationCoordinate2DではなくCLLocationを渡している - ジオコーディング結果の要素にアクセスする前に、配列が空でないことを確認している
- ジオコーディング呼び出しで、
do/catchを使った適切なasync/awaitエラー処理を行っている - 座標と住所の両方がわかっている場合、複数の表現を指定している
- サービス間の相互運用が必要な場合、Supporting representation にサービス識別子を含めている
相互参照
- MapKit のマップビューとアノテーションについては、MapKit のドキュメントを参照してください
- CoreLocation の権限と位置情報の更新については、CoreLocation のドキュメントを参照してください
- マップとの SwiftUI の統合については、SwiftUI の
Mapビューを参照してください
参考資料
コメント
Markdown 対応