iOS9.3で追加されたMKLocalSearchCompleterを試す

# はじめに
iOS9.3で追加された[`MKLocalSearchCompleter`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKLocalSearchCompleter_class/index.html)がちょっと便利だったので、どんな風に使えばいいのかと ローカル検索(`MKLocalSearch`)と合わせて使う方法をサンプルコードを用いて解説します。ローカル検索がなんだかわからない人は[コチラ](http://qiita.com/koogawa/items/23359821d4c489c0ca5e)が参考になるかと思います。

## 実行環境
* iOS: Version 9.3
* Swift: Version 2.2
* Xcode: Version 7.3

# MKLocalSearchCompleterとは
最初ドキュメントをパッと見たときにはローカル検索と何が違うんだろうと思ってしまったのですが、よく読むとこいつは検索結果をあらかじめ確認してプロットさせたい時などに使える、クエリの補完機能を提供するものでした。

> An MKLocalSearchCompleter object takes a partial search string and generates a list of potential completions. 

今までありきたりな名前のお店を検索した時などに、求めていない場所まで地図にプロットされていたものを事前にユーザに絞らせてから検索させることができます。

# サンプルコード
Appleが提供しているマップのように、検索窓に文字列を入力するタイミングで候補となる場所をテーブルビューに表示させるサンプルを作りました。一応[ここ](https://github.com/hanawat/local-search-completer)にもあげておきます。

## テーブルビューに場所の候補を表示
`MKLocalSearchCompleterDelegate`のデリゲートを自身に設定します。こいつは検索結果がアップデートされる(またはエラー)タイミングで何か処理をさせることができます。

また`filterType`は`LocationsAndQueries`と`LocationsOnly`があり、前者は場所の名前と一般的な用語(ex. Cof -> Coffee)も一緒に補完されます。後者は場所の名前のみです。

```swift:ViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()

        completer.delegate = self
        completer.filterType = .LocationsOnly
    }
```

検索窓の`shouldChangeTextInRange`で入力された文字を`queryFragment`に突っ込んでいます。こいつに入った段階で自動的に非同期で検索が行われているようです。適度にユーザの入力がされてから検索させる為にディレイもしてくれているみたいです。

> The completer object waits a short amount of time before initiating new searches. This delay gives you enough time to update the search string based on typed input from the user. 

```swift:ViewController.swift
    func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {

        guard let text = searchBar.text else { return false }

        completer.queryFragment = text
        return true
    }
```

先ほど設定した`MKLocalSearchCompleterDelegate`が検索完了時に　`completerDidUpdateResults:`を呼んでくれるので、ここにテーブルのリロード処理を入れておけば検索結果が常時反映されます。

```swift:ViewController.swift
    func completerDidUpdateResults(completer: MKLocalSearchCompleter) {

        tableView.reloadData()
    }
```

`completer.results`に検索結果の`[MKLocalSearchCompletion]`が入っているので、それぞれ取り出してセルのテキストラベルに入れればおしまいです。

```swift:ViewController.swift
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let identifier = "Cell"
        var cell = tableView.dequeueReusableCellWithIdentifier(identifier)

        if (cell == nil) {
            cell = UITableViewCell(style: .Default, reuseIdentifier: identifier)
        }

        cell?.textLabel?.text = completer.results[indexPath.row].title
        return cell!
    }
```

<img width="423" alt="スクリーンショット 2016-03-30 11.28.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/92c4ecef-c31c-83e6-6540-6c622a2be752.png">


## マップにアノテーションとして追加

ローカル検索のリクエスト`MKLocalSearchRequest`のイニシャライザに`MKLocalSearchCompletion`から生成できるものが追加されたのでこれを使います。

```swift:ViewController.swift
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        guard let mapViewController = storyboard.instantiateViewControllerWithIdentifier("MapViewController") as? MapViewController else { return }

        mapViewController.request = MKLocalSearchRequest(completion: completer.results[indexPath.row])
        self.showViewController(mapViewController, sender: nil)
    }
```

あとは受け取ったマップ画面でローカル検索をするだけです。ちょっと便利になりましたね。

```swift:MapViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()

        let search = MKLocalSearch(request: request)

        search.startWithCompletionHandler { response, error in
            response?.mapItems.forEach { item in

                let point = MKPointAnnotation()
                point.coordinate = item.placemark.coordinate
                point.title = item.placemark.title
                self.mapView.addAnnotation(point)
            }

            self.mapView.showAnnotations(self.mapView.annotations, animated: true)
        }
    }
```

<img width="423" alt="スクリーンショット 2016-03-30 11.29.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/4163dde1-961d-49c1-cc73-d0e85b3da7d3.png">