【SwiftUI】ジオコーディングの実装方法!MapKitで逆ジオコーディング

この記事からわかること
- Swift UIで地図(Maps)を表示する方法
- MapKitフレームワークの使い方
- ジオコーディング/逆ジオコーディングの実装方法
- CLGeocoderやCLPlacemarkとは?
- 非同期のジオコーディングをグローバル変数に格納する方法
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.0
- iOS:18.0
- Swift:5.9
- macOS:Sonoma 14.6.1
今回はSwiftUIでMapKitフレームワークを使ったジオコーディング/逆ジオコーディングの実装方法をまとめていきます。MapKitの使い方や地図の表示方法については別記事で解説していますので参考にしてください。
ジオコーディング/逆ジオコーディングとは?
- ジオコーディング :住所→緯度経度 に変換
- 逆ジオコーディング:緯度経度→住所 に変換すること
Map機能の根幹にもなりますが、住所を入力して地図上で位置を示すことができるのは内部的にジオコーディング(住所→緯度経度)が行われその座標に基づいた場所を特定できるからです。
住所は人向けに、座標は機械向けに定義された位置情報であり、両者が紐付けされていることでどちらにも扱いやすくなっています。
ちなみにGoogleマップではマウスの右クリックで選択した場所の緯度経度が表示されるようになっています。

Swiftでジオコーディング/逆ジオコーディングを実装するには?
SwiftではMapKit
フレームワークをインポートするだけで簡単に地図を表示したり、操作することが可能です。MapKit
フレームワークにはCoreLocation
フレームワークが組み込まれており、その中に搭載されたCLGeocoder
クラスを使用することでジオコーディング/逆ジオコーディングを実装することができます。
CLGeocoder
CLGeocoder
クラスは座標と住所などの位置情報を相互に変換してくれるクラスです。メソッドにジオコーディングが可能なgeocodeAddressString
や逆ジオコーディングが可能なreverseGeocodeLocation
などが用意されており住所や座標を渡すだけで様々な位置情報を持ったオブジェクトを返してくれます。
geocodeAddressString
geocodeAddressString
メソッドは非同期でジオコーディングリクエストをサーバーへ送信し結果を取得できるメソッドです。非同期で行われるので結果を取得できるタイミングには注意してください。
イニシャライザは以下の通りです。
addressString
座標などの位置情報へ変換したい住所を渡す引数です。
対象の住所を文字列型(「東京都墨田区押上1丁目1−2」など)として渡すだけでOKです。
completionHandle
ジオコーディングした結果を取得するためのハンドラーブロックです。
CLGeocodeCompletionHandler
型はタイプエイリアスなので実際は([CLPlacemark]?, Error?) -> Void
の形式のクロージャになります。
取得に成功した場合は該当の情報を持ったCLPlacemark
オブジェクトが配列形式で参照できるようになります。配列ですが基本的には1つの要素しか格納されていません。
失敗した場合はError
に該当のエラーメッセージなどが格納されます。また両方とも?
がついているのでnil
が許容されています。
おすすめ記事:Optional(オプショナル)型とnil
CLPlacemark
結果の返り値であるCLPlacemark
オブジェクトはNSObject
クラスに準拠したオブジェクトです。
CLPlacemark
オブジェクトのプロパティとしてその位置情報に基づいた住所や郵便番号、座標などを取得することができるようになります。
プロパティ名 | 概略 |
---|---|
location | CLLocationオブジェクト(※) |
name | 名前 |
isoCountryCode | ISO国コード |
country | 国名 |
postalCode | 郵便番号 |
administrativeArea | 都道府県 |
subAdministrativeArea | 郡 |
locality | 市区町村 |
subLocality | 丁番なしの地名 |
thoroughfare | 丁目がある場合、それを含む地名 |
subThoroughfare | 番地 |
region | CLRegionオブジェクト |
timeZone | TimeZoneオブジェクト |
inlandWater | 内陸水 |
ocean | 海名 |
areasOfInterest | 目印に関連する関連分野 |
※:CLLocationオブジェクト
は座標や緯度、経度などをプロパティとして保持しているオブジェクトです。
SwiftUIでのジオコーディング
では実際にジオコーディングできるコードを実装してみます。フレームワークはSwiftUIでも問題なく動作させることができるので実際にやってみたいと思います。まずはMapKit
のインポートを忘れずに記述しておきます。
続いて管理しやすくするためにConversionSpot
クラスを作成し、その中にジオコーディングの処理を記述していきます。
CLGeocoder
クラスが使えるようになっているのでインスタンス化しジオコーディングするためのgeocodeAddressString
メソッドを呼び出します。nil
であることも顧慮してオプショナルバインディングで展開し配列の1番目を参照します。
緯度と軽度にはlocation
プロパティからCLLocationオブジェクト
に参照し、その中のcoordinate
(= 座標)の中から参照することができます。
このままでは表示できないので下記のようなUI部分を作成し実行してみます。
全体のコード
SwiftUIでの逆ジオコーディング
続いて逆ジオコーディングする場合を見てみます。基本的なポイントと流れは先ほどと同様で渡す位置情報と呼び出すメソッドが異なります。
先ほどは住所でしたが逆ジオコーディングでは緯度経度を準備します。ただ数字の羅列を渡すわけではなくCLLocation
オブジェクトに倣った形式で渡す必要があるので以下のように定義します。
reverseGeocodeLocation
メソッドのイニシャライザは以下の通りです。
では実際に実装してみます。先ほどのConversionSpot
クラスにregeocoding
メソッドを追加してみます。
忘れないように呼び出すメソッドを変更して実行してみます。
正しい郵便番号を取得することができました。
非同期のジオコーディングをグローバル変数に格納する
非同期で行われるgeocodeAddressString
メソッドなどはデバッグエリアへのプリントは簡単に可能ですが、変数などに格納し、実際にアプリ画面に表示させるとなると取得タイミングがわからない以上容易ではありません。色々模索してみたのですが知識が足りずググってみたところ解決策が「teratail」にありました。
そのコードを参考に、SwiftUIでも動作するようにしたのが以下のコードになります。
ここには私の知らない知識やコードがたくさんありましたのでまた時間をかけて読み解いてみたいと思います。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。
私がSwift UI学習に使用した参考書