Objective-C
Xcode
iOS
Swift

<Swift> MKLocalSearchでプレイス検索


TL;DR

iOS標準Mapkitには、実は、自然言語(例:コンビニ 新宿)でプレイス検索ができるMKLocalSearchが存在します!

Google Places APIほどの精度はありませんが、クレカ登録などの面倒な手続きを踏まずに利用できる便利なAPIです。

その実装方法を紹介します。Swift 4.2です。


更新


  • 2019/02/14 コードの整理とSwift 4.2対応


準備

まずMKPlacemarkを拡張します。

MKPlacemarkは、都道府県、市区町村、番地などのプロパティを持っていますが、

「東京都渋谷区千駄ヶ谷1-2-3」のような出力をするには、これらひとつひとつ連結しなくてはなりません。

そこでaddressというComputed Propertyを持たせます。

import MapKit

extension MKPlacemark {

var address: String {
let components = [self.administrativeArea, self.locality, self.thoroughfare, self.subThoroughfare]
return components.compactMap { $0 }.joined(separator: "")
}
}

英語の場合は、↓の表記ですね。

return components.reversed().compactMap{ $0 }.joined(separator: ",")


検索メソッド

ここで用意した引数は2つです。

queryには自然言語(例:コンビニ 新宿)を渡し、regionにはMKCoordinateRegionを渡すとヒントとして利用され、検索精度の向上が見込めます(任意)。

struct Map {

enum Result<T> {
case success(T)
case failure(Error)
}

static func search(query: String, region: MKCoordinateRegion? = nil, completionHandler: @escaping (Result<[MKMapItem]>) -> Void) {
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = query

if let region = region {
request.region = region
}

MKLocalSearch(request: request).start { (response, error) in
if let error = error {
completionHandler(.failure(error))
return
}
completionHandler(.success(response?.mapItems ?? []))
}
}
}


基準となるRegionを渡し、検索実行!

検索には、座標と検索範囲のregionを設定します。

let coordinate = CLLocationCoordinate2DMake(35.6598051, 139.7036661) // 渋谷ヒカリエ

let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000.0, longitudinalMeters: 1000.0) // 1km * 1km

Map.search(query: "コンビニ", region: region) { (result) in
switch result {
case .success(let mapItems):
for map in mapItems {
print("name: \(map.name ?? "no name")")
print("coordinate: \(map.placemark.coordinate.latitude) \(map.placemark.coordinate.latitude)")
print("address \(map.placemark.address)")
}
case .failure(let error):
print("error \(error.localizedDescription)")
}
}


結果

[MKMapItem]が検索結果として返ってきます

#map name: ローソン渋谷三丁目店

#map coordinate: 35.6560398 35.6560398
#map address 東京都渋谷区渋谷3丁目28番8号
#map name: ファミリーマート麹町二丁目店
#map coordinate: 35.6834507 35.6834507
#map address 東京都千代田区麹町2丁目5番4号
#map name: ナチュラルローソン&フードクルック 神宮外苑西店
#map coordinate: 35.671825 35.671825
#map address 東京都渋谷区神宮前3丁目35番8号

あとは、Mapにannotationを立てるなり、Cellに表示するなりしてください!