はじめに
MapKitは、appleより提供されている地図や衛星写真を取り扱うためのフレームワークです。
UI上に地図を表示する以外にも様々なことが可能です。
今回は、入力中に候補となる地点情報を提案する「サジェスト機能」としての使い方を紹介します。
環境
- OS: macOS Sonoma 14.7.1
- XCode: 16.2.0
サジェスト内容を取得する
構成
以下のような構成でアプリを作成することにしてみます。
なお、ContentView
とContentViewModel
、SuggestModel
に関しては今回の主題と外れるので、完成したコードのみ示し、主にSuggestRepositoryに付いて解説します。
ContentView
Viewの描画を行う。
入力欄と、サジェスト内容のリストを持つ
struct ContentView: View {
@State var viewModel = ContentViewModel()
var body: some View {
VStack {
TextField("検索", text: $viewModel.searchText)
.onChange(of: viewModel.searchText) {
Task { await viewModel.requestSuggets() }
}
List(viewModel.suggests, id: \.self) { suggest in
VStack(alignment: .leading) {
Text("名前: \(suggest.name)")
Text("緯度: \(suggest.latitude)")
Text("軽度: \(suggest.longitude)")
}
}
}
.padding()
}
}
ContentViewModel
ContentViewのViewModel。
サジェスト内容の一覧suggests
や検索窓の入力内容searchText
を持つ
@MainActor
@Observable
final class ContentViewModel {
var suggests = [SuggestModel]()
var searchText: String = ""
/// SuggestRepositoryからサジェスト内容の一覧を受けとり、`suggests`を更新する
func requestSuggets() async {
let suggests = await SuggestRepository.shared.requestSuggests(query: searchText)
self.suggests = suggests
}
}
SuggestModel
サジェスト内容を保持する。
SuggestModelの配列をSugegstRepositoryから返すことでContentViewModelにサジェスト情報を伝える。
今回はサンプルとして場所の名前と緯度経度を返すことにする。
struct SuggestModel: Hashable {
var name: String
var latitude: Double
var longitude: Double
}
SuggestRepositoryの実装
実装自体はシンプルなので、まずコードを示します。
import MapKit
actor SuggestRepository {
static let shared = SuggestRepository()
private init() {}
func requestSuggests(query: String) async -> [SuggestModel] {
let localSearch = MKLocalSearch.Request()
localSearch.naturalLanguageQuery = query
let response = try? await MKLocalSearch(request: localSearch).start()
guard let mapItems = response?.mapItems else { return [] }
let suggests = mapItems.compactMap {
SuggestModel(
name: $0.name ?? "",
latitude: $0.placemark.coordinate.latitude,
longitude: $0.placemark.coordinate.longitude
)
}
return suggests
}
}
今回はシングルトンなactorで実装してみました。
requestSuggests(query:)
は、検索対象の文字列を受取り、サジェスト結果を配列で返す関数です。
実装手順としては3stepです。
step1: リクエスト内容を設定
let localSearch = MKLocalSearch.Request()
localSearch.naturalLanguageQuery = query
まずは、上記の部分です。
MKLocalSearch.Request
という型がMapKitから提供されています。
このクラスのプロパティに検索内容を設定することで様々な条件の地点情報を取得できます。
- naturalLanguageQuery: 文字列でサジェスト内容を絞り込めます
- addressFilter: 検索結果に含める、または除外する位置情報を設定できます
- pointOfInterestFilter: 検索結果煮含める、または除外するカテゴリを設定できます
- etc
step2: リクエストを実行
let response = try? await MKLocalSearch(request: localSearch).start()
1行でサジェスト内容を取得できます。
今回はasync/awaitに対応したメゾットを利用しましたが、completion handlerのメゾットも用意されています。
step3: 整形
guard let mapItems = response?.mapItems else { return [] }
let suggests = mapItems.compactMap {
SuggestModel(
name: $0.name ?? "",
latitude: $0.placemark.coordinate.latitude,
longitude: $0.placemark.coordinate.longitude
)
}
return suggests
リクエストの実行結果はMKLocalSearch.Response
型です。
プロパティとしてmapItems
が存在し、ここを参照することでMKMapItem
の配列を得ることができます。
MKMapItem
は、サジェストされた位置情報の名前や緯度経度、その他様々な情報が入っています。
必要な情報を取り出し、整形してreturnしましょう!
あとはSuggestsModelをViewに反映すれば、位置情報サジェストを行いことができます!
まとめ
MapKitは地図の描画以外にも地点情報の取得などを簡単に実装できる、非常に便利なフレームワークです。
外部APIと比べると精度や網羅性で劣る部分もありますが、外部サービスに依存せず無料で利用できることが最大の利点かと思います。
是非利用してみてください!