Help us understand the problem. What is going on with this article?

<Swift> MKLocalSearchでプレイス検索

More than 1 year has passed since last update.

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に表示するなりしてください!

globis
グロービスは 1992 年の創業以来、社会人を対象とした MBA、人材育成の領域で Ed-Tech サービスを提供し、現在は日本 No.1 の実績があります。これらの資産と、さらに IT や AI を活用することで、アジア No.1 を目指しています。
http://www.globis.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした