現在地を取得する

## ゴール
現在地を取得して表示する。

#### 実装
場所をユーザー側から検索できるようにするには Places SDK for ios を使用する必要があるらしい。Places SDK for ios の他に Places SDK もあるのだが、粒度がわからない。とりあえずは意識しないでおこうと思う。

[こちら](https://developers.google.com/maps/documentation/places/ios-sdk/start#use-cocoapods) を参考に、現在地を表示させてみる。
AppDelegadeにPlaces APIを使いますよのAPIキーを追加


```swift:AppDelegade.swift
    func application(_ application: UIApplication, didFinishLaunchingWithOptions 
    launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        GMSServices.provideAPIKey("APIKEY")
        GMSPlacesClient.provideAPIKey("APIKEY")
        return true
    }
```

追加して実行すると、google maspsの方はv5.0.0使えますよとのことだったので、

```
New version of Google Maps SDK for iOS and Google Places SDK for iOS available: 5.0.0.0
```

[こちら](https://developers.google.com/maps/documentation/ios-sdk/upgrade) に従ってアップデートする。
アップデートが完了して実行すると、上のログが出なくなった！

次に、ViewControllerに現在地を取得するPGを実装する。
ViewControllerクラス配下にラベル表示のための変数と[GMSPlacesClient](https://developers.google.com/maps/documentation/places/ios-sdk/reference/interface_g_m_s_places_client)型の変数を定義（[swiftの変数定義](https://qiita.com/og_omochi/items/0b255c790c4c3aecc630#:~:text=%E3%81%93%E3%81%AE%EF%BC%81,%E6%84%8F%E5%91%B3%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%A7%E3%81%97%E3%82%87%E3%81%86%E3%80%82)）

```swift
@IBOutlet private var nameLabel: UILabel!
@IBOutlet private var addressLabel: UILabel!
private var placesClient: GMSPlacesClient!
```

ViewDidLoadの中で、placesClientに値を格納する。
GoogleMapsの方では行わなかったが、Placesでは、ここでAPIキーの認証を行うっぽい。

```swift
placesClient = GMSPlacesClient.shared()
```

ボタンを押下すると現在地を取得するメソッドを追加する。

```swift
@IBAction func getCurrentPlace(_ sender: UIButton) {
        let placeFields: GMSPlaceField = [.name, .formattedAddress]
        placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: placeFields) { 
        [weak self] (placeLikelihoods, error) in
        guard let strongSelf = self else 
        {
          return
        }

        guard error == nil else {
          print("Current place error: \(error?.localizedDescription ?? "")")
          return
        }

        guard let place = placeLikelihoods?.first?.place else {
            strongSelf.nameLabel.text = "No current place"
            strongSelf.addressLabel.text = ""
            return
        }

        strongSelf.nameLabel.text = place.name
        strongSelf.addressLabel.text = place.formattedAddress
    }
}
```

次に、storyboard側でUIを実装していこうと思うと、2年前くらいに触っていたときの仕様と変わっていたので、[こちら](https://qiita.com/kanae_/items/e4c74ab1de7ccc9125bb)を参考にボタン*1、ラベル*2を配置する。
![スクリーンショット 2021-05-15 14.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391844/864d47a0-79e6-e72d-1269-25b70e6c75cb.png)
配置して実行するが、問題が複数発生。
1. マップが最前面に配置されるため、ボタンやラベルが表示されない。→マップ描画部分をコメントアウトして一時的な対応
1. 以下エラーで自己位置を取得できない。
   
```:Error
Current place error: The operation couldn’t be completed. The Places SDK could 
not find the user's location. This may be because the user has not allowed the 
application to access location information.
```
   
   PC上のシミュレータではGPSをしようする機能に対応していない or 設定でこのアプリケーションに位置情報の取得 
   を許可する必要があるがある。→シミュレータを使用せず、iPhone上で動かしてみようとする。
1. iPhone上で実行できない。

```:Error
The operation couldn’t be completed. Unable to log in with account 'メアド'. The 
login details for account 'メアド' were rejected.
No profiles for 'プロジェクト名' were found: Xcode couldn't find any iOS App 
Development provisioning profiles matching 'プロジェクト名'.
```

#### バグ取り
まず、シミュレーターで実行することを考える。

```:Error
Current place error: The operation couldn’t be completed. The Places SDK could 
not find the user's location. This may be because the user has not allowed the 
application to access location information.
```
こいつをどうにかしたい。
まず、PG中で`findPlaceLikelihoodsFromCurrentLocation`メソッドを使うには、位置情報サービスの許可がマスト[らしい](https://developers.google.com/maps/documentation/places/ios-sdk/current-place)（先に言え）。
[このあたり](https://stackoverflow.com/questions/38915951/this-may-be-because-the-user-has-not-allowed-the-application-to-access-location)を参考に実装した。

```swift:ViewDidLoad()
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
```

すると以下の注意。

```
This app has attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain both “NSLocationAlwaysAndWhenInUseUsageDescription” and “NSLocationWhenInUseUsageDescription” keys with string values explaining to the user how the app uses this data
```

センシティブなデータ使うんだから説明責任があるぞ！ってことだと理解した。info.plistに`NSLocationAlwaysAndWhenInUseUsageDescription` `NSLocationWhenInUseUsageDescription`に`Show your location on the map`（使わせてください）を追加して、さて実行。

```:Error
Current place error: The operation couldn’t be completed. The Places SDK could 
not find the user's location. This may be because the user has not allowed the 
application to access location information.
```

消えない。確かにアプリ実行時にこちらから位置情報を許可できていない。
[こちら](https://github.com/Baseflow/flutter-geolocator/issues/172)や[こちら](https://stackoverflow.com/questions/24842594/ios-simulator-location-not-working)を参考に、info.plistに`NSLocationAlwaysUsageDescription`追加したり、Custom Locationを自宅で設定してみたりしたが不発

埒が明かないので、実機で動かしてみよう！
実機で実行する際に詰まっていたエラーは調べてみると一瞬で解決。

- [アカウント情報のセッション有効機嫌切れ](https://god48.com/xcode-device-build-error)
- [信頼されていないデベロッパの信頼](https://support.okageregi.com/408265-%E4%BF%A1%E9%A0%BC%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%82%A8%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A4%E3%82%BA%E3%83%87%E3%83%99%E3%83%AD%E3%83%83%E3%83%91%E3%81%A8%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%82%8B)

実機で実行！やっぱり同じエラー。Location Informationが許可されていませんと。
「許可しますか？」のポップアップも出てこないので、この許可周りが怪しそう。調べる。

- [Local Informasionの設定](https://qiita.com/chino_tweet/items/db3a536234a43a3c31d9)

```swift:ViewController.swift
private var placesClient: GMSPlacesClient!

func setupLocationManager(){
    locationManager = CLLocationManager()
    guard let locationManager = locationManager else { return }
        
    locationManager.requestAlwaysAuthorization()
        
    let status = locationManager.authorizationStatus
    if(status == .authorizedAlways){
        locationManager.delegate = self
        locationManager.distanceFilter = 10
        locationManager.startUpdatingLocation()
    }
}
```
LocationManagerを関数化して、ViewDidLoadで呼び出す。
ポップアップが出てきて、やっと動きました。
Simulator、実機ともに成功。
![スクリーンショット 2021-05-16 19.19.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391844/e225b5c1-f1de-9f75-92c7-5f3eacbb568f.png)
Simulate Locationをロンドンに設定しているため、ロンドンの住所を取得している。

追加した部分としては、以下3行。

```swift
locationManager.delegate = self
locationManager.distanceFilter = 10
locationManager.startUpdatingLocation()
```

それぞれの役割は以下の通り。（[参考](https://iphone-tora.sakura.ne.jp/cllocationmanager.html)）

- delegate : 
デリゲートを指定するとのことだが、デリゲートとは？状態。追記する。
- distanceFilter :  
位置情報取得間隔。小さい値を設定すると、一箇所にとどまっている時にも、位置情報が通知され続けます。3-5m以上を設定すると使い勝手が良いイメージ[らしい](https://qiita.com/ebi-toro/items/9f0faff5b614b301fb11)。従って、なくても実行可。
- StartUpdatingLocation() : 
GPSの使用を開始する。

実際に、StartUpdatingLocationを入れないと、現在地が取れない。
何はともあれ、現在地は取れるようになった。
ちなみに、本当に現在地を取得しようと思うと、できるのは実機だけです。
ちゃんちゃん。次こそは、検索した場所にピンを刺します。



#### メモ

- [CLLocation周りの変更点（許可ステータスによる分岐など）](https://qiita.com/kj_trsm/items/66e07c61ce4a3142832f)
- [シミュレーションだと機能しない???](http://swift-salaryman.com/cllocationmanager.php)
- [位置情報に取得に関する情報](https://dev.classmethod.jp/articles/ios-corelocation-swift3/)