【Swift】MKLocalSearchの使い方。店名やキーワードで検索してピンを刺す。(Swift 2.1、XCode 7.2)

2020年6月16日

店名やキーワードで目的地を探す

過去の記事で、住所を座標に変換してピンを刺す方法を説明した。⇒「記事

ピンのサブタイトルに住所を設定

 

住所から目的地を探す使い方の他に、店名やキーワードで周辺の目的地を探すことも多そうだ。そこで、本記事では店名やキーワードから周辺の目的地を検索してピンを刺す方法を説明する。

以降の手順は「Map Kit Viewの使い方」の続きから行うので、実装を試してみる人は先に読んでおくことをお勧めする。

 

MKLocalSearchを使って周辺検索をする

デバイス画面にSearch Bar(以下、検索バー)を配置する(下図赤矢印)。黄緑枠のアシスタントエディタボタンを押してViewController.swiftを開く。Ctrlキーを押しながら検索バーをドラッグ&ドロップでソースコードまで運んで吹き出しの設定画面を表示する。

Connectionに「Outlet」、Nameに「testSearchBar」を入力してConnectボタンを押す。これでソースコードから検索バーを操作できるようになった。

デバイス画面に検索バーを配置する。

 

ViewController.swiftを以下のコードに変更する。

店名やキーワードから目的地を探すにはMKLocalSearchクラスを利用する。startWithCompletionHandlerメソッドを引数「検索条件」と「クロージャ」で呼び出すだけで周辺の建物を検索してくれる優れもの。「クロージャ」には変換結果エラー情報の2つの変数が渡ってくるので、エラー情報に何も設定されていないときは検索成功としてピンを刺している。

クロージャにについて詳しくは次の記事を参照されたし。⇒「クロージャとは

 

以下は実際のプレイ動画

 

MKLocalSearchクラスを使ってみたところ、下図のように1回の検索で抽出されるデータは最大10件までで、毎回同じ検索結果になるとは限らない。また、検索範囲を狭くしても範囲外にあるデータも検索結果に入ってくる結果となった。

もしかすると検索結果を増やす術があるのかも知れないので、分かり次第追記する。何か知っている方は教えて頂けると幸いである。

検索結果を比較する

Google Place APIを使って周辺検索をする

仮に上記のような仕様の場合、周辺検索としては物足りなさを感じてしまう。そこで、マップやピンはSwiftの部品を使い、周辺検索の部分だけGoogleが提供しているGoogle Places APIを利用して実装してみよう。

Google Places APIとは、Googleマップと同じデータベースにアプリからアクセスできるAPIである。現在では旅行や飲み会、デートまで、どんなときでもGoogleマップが利用されるマップの頂点に立つ存在だ。

利用するには下準備が必要となる。公式ガイドに手順が載っているので参照されたし。⇒「公式ガイド

準備でハマりそうな点を以下にメモしておく(公式ガイドは2016年5月現在のものを使用)。

ハマりそうな点1

ステップ3の「pod install」を実行するときに、「プロジェクト名.xcodeproj」のファイルが「PodFile」と同じ場所に無いと以下のエラーが発生する。

[!] Unable to find the Xcode project /Users/test/Documents/testPlayGround/UseLocation4/.xcodeproj.xcodeproj for the target Pods.

以下のように「PodFile」にプロジェクトファイルへのパスを記述すればエラーは解消する。

 

ハマりそうな点2

準備が終わったらXCodeを再起動する必要がある。その際、「プロジェクト名.xcodeproj」では無く「プロジェクト名.xcworkspace」をダブルクリックして起動すること。

 

ハマりそうな点3

ステップ4で「Google Places API for iOS」 と 「Google Maps SDK for iOS」を有効にすると記載されているが、筆者のシミュレーターを使ったテスト環境では「Google Places API Web Service」も有効にしないと以下のエラーが発生して検索エラーが発生する。

“error_message" = “This API project is not authorized to use this API. Please ensure that this API is activated in the APIs Console: https://console.developers.google.com/apis/library?project=_ .."

 

ハマりそうな点4

ステップ5で[iOS key] を作るように記載されているが、筆者のシミュレーターを使った環境ではiOSキーを使うと以下のエラーが発生して検索に失敗し、「サーバーキー」を使うとエラーは解消する。次のページが参考になった。⇒「This IP, site or mobile…

“error_message" = “This IP, site or mobile application is not authorized to use this API key. Request received from IP address 122.16.221.25, with empty referer";

 

ハマりそうな点5

AppDelegate.swiftで取得したキーをGMSServicesに設定するのを忘れないこと。

GMSServices.provideAPIKey(“取得したキー")

ハマりそうな点は以上。

 

次に、ViewController.swiftを以下のコードに変更する。

検索ボタンが押された段階で検索URLを作成してGoogleに問い合わせを行っている。GoogleからはJSON形式のデータが検索結果として返ってくるので、データの件数ぶんのピンを作成する。

検索結果は最大60件のデータが1回20件に分割されて返ってくる。検索結果のnext_page_tokenに値が設定されているときは続きがあることを意味しているので、それをもとにGoogleに再問い合わせを行う作りにしている。

また、Google側で検索結果の切り替えに若干の時間を要するため、時間を空けずに連続で問い合わせると検索結果無しが返ってきてしまう。なので、1秒のスリープを入れてから再問い合わせを行っている。

検索処理はメインスレッドの処理とは非同期で行われる。そのため、検索処理の中でマップビューにピンを追加すると、画面を指でタッチしないとピンが描画されないという事象が発生した。そこで、セマフォを使って検索処理とメインスレッドの処理を同期させ、検索処理がすべて終わった段階でマップビューにピンを追加する仕様にした。

 

以下は実際のプレイ動画。60本のピンが刺さった。