LoginSignup
5
7

More than 3 years have passed since last update.

iOSアプリで位置情報を取得するときに配慮する点をまとめてみた③

Last updated at Posted at 2021-01-12

前回の続き↓
iOSアプリで位置情報を取得するときに配慮する点をまとめてみた②

はじめに

iOSアプリで位置情報を取得するときに配慮する点をまとめた内容となっています。

前回までのあらすじ

前回、アプリの位置情報サービスの有無バックグラウンド状態から戻った時も
チェックをしなければいけないと書きました。

アプリがバックグラウンド状態から戻った時にある処理をするにはAppDelegateapplicationWillEnterForeground(_ application: UIApplication)で処理するんでしたね。

そして、アラートを表示したいのでViewController内で処理するためにNotificationCenterを使いました。

バックグラウンド状態から戻ったときにチェックする(アプリ)

では、実際にコードを書いてみます。
※長くなるので前回のソースコードから一部抜粋

今回も、アラートの表示はこの方法を用います↓
UIAlertControllerをファイルを分けて実装してみる

ViewController
import UIKit
import CoreLocation

final class ViewController: UIViewController {

   private var locationManager: CLLocationManager = {
        var locationManager = CLLocationManager()
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.distanceFilter = 5
        return locationManager
    }()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    // バックグラウンド状態から戻ってきた時に呼ばれるメソッド(自分で作る)
    @objc private func willEnterForeground() {
        // 端末の位置情報サービスがオンの場合      
        if CLLocationManager.locationServicesEnabled() {
            // 位置情報サービスの認証ステータスを取得
            let status = CLLocationManager.authorizationStatus()

            switch status {

            // 許可しない場合
            case .denied:
                // アラート表示
                Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                    // 設定アプリに画面遷移する
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                }

            default:
                break
            }
        }

    // 端末の位置情報サービスがオフの場合
    }else {
        // アラート表示
        Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます") 
    }
}

端末の位置情報サービスがオンかオフかを分岐して
オンの場合にアプリの位置情報サービスの有無をチェックしています。

ビルドしてみると、ちゃんとチェックされてますね↓

※バグ?のせいなのか、たまに位置情報サービスの認証ステータスが次回確認になっていますが
本来はなしになります。
ezgif.com-gif-maker3.gif
因みに、この部分は.deniedだけのケースのみでOKです。

理由は認証ステータスが変更されればデリゲートメソッドが呼ばれますし、設定アプリに画面遷移するのは.deniedの場合のみで、その時に何もしないで戻った場合をチェックしたいので他の認証ステータスの記述は必要ないです。

ここで、一旦、全体のソースコードを見てみましょう。

ViewController
import UIKit
import CoreLocation

final class ViewController: UIViewController {

    private var locationManager: CLLocationManager = {
       var locationManager = CLLocationManager()
       locationManager.distanceFilter = 5
       locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
       return locationManager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    @objc private func willEnterForeground() {     
        if CLLocationManager.locationServicesEnabled() {

            let status = CLLocationManager.authorizationStatus()

            switch status {

            case .denied:
                Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                }

            default:
                break
            }

    }else {
        Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます") 
    }
  }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
          if CLLocationManager.locationServicesEnabled() {

        let status = manager.authorizationStatus

        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            manager.startUpdatingLocation()

        case .notDetermined:
            manager.requestWhenInUseAuthorization()

        case .denied:
            Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
            }

        case .restricted:
            break           

        default:
            break
        }
      }else {
         Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます") 
      }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let gps = manager.location?.coordinate else {
            return
        }
        manager.stopUpdatingLocation()
        let lat = gps.latitude
        let lng = gps.longitude
        print("経度:\(String(describing: lat)), 緯度:\(String(describing: lng))")
    }
}
Alert
import UIKit

final class Alert {
    static func okAlert(vc: UIViewController,title: String, message: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let okAlertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
        okAlertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: handler))
        vc.present(okAlertVC, animated: true, completion: nil)
    }
}

端末・アプリの位置情報サービスの有無や、変更された認証ステータスの処理等ここまで
色んな所に配慮してきました。

後は、お気づきの方がいらっしゃると思いますが
認証ステータスが.restrictedの場合は何もしていませんね。

ここは、Appleのドキュメントを見てみるとペアレンタルコントロールなどの何らかの制限があるために
ユーザーが認証ステータスを変更できない状態に呼ばれる認証ステータスとなっています↓
CLAuthorization Status .restricted

ずっとbreakにしていましたが、アラートなどでユーザーに伝えたほうがいいので改修しときましょう。

ViewController
      case .restricted:
          Alert.okAlert(vc: self, title: "位置情報サービスの使用を\n許可されていません", message: "何らかの制限が掛かっています")

iOS14以外のバージョン対応

現状、iOS14以降を想定して位置情報の取得をしてきましたが
iOS14以外のバージョンでも位置情報を取得したい場合が開発していると出てくると思います。

その場合、使用するメソッドが違ったり、Appleが非推奨にしているコード等も登場して
多少、複雑になってきます。

では、実際にアプリのバージョンを下げてみて実装していきましょう。

私はこちらの記事を拝見して、iOSのバージョンを下げましたので参考にしてみて下さい↓
世界一詳細に全部日本語でXcode11で作った新規プロジェクトをiOS12以前で実行できるところまで解説する

推奨・非推奨コード

では、これでiOS11 ~ iOS14に対応するアプリとなりました。
実装する前にAppleが推奨・非推奨しているコードを紹介していきます。

※私の記事に関係があるコードだけを紹介しますのでご了承ください。

// CLLocationManagerクラスのインスタンス初期化および、認証ステータスが変更されたら呼ばれるデリゲートメソッド

// 推奨していない iOS 4.2–14.0
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)

// 推奨している iOS14-
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
// 位置情報の認証ステータス取得

// 推奨していない iOS 4.2–14.0
func authorizationStatus() -> CLAuthorizationStatus

// 推奨している iOS14-
var authorizationStatus: CLAuthorizationStatus

こんな感じに変更されています。

後は、iOS14から正確な位置精度という項目が新たに追加されました。
簡単に言うと、位置情報の精度を正確にするか曖昧にするかを自分で選択できるようになったんですね。

下記のコードのように、選択肢によって分岐することができます↓

switch locationManager.accuracyAuthorization {

      // 正確な位置情報を取得した場合  
      case .fullAccuracy:

      // 曖昧な位置情報を取得した場合 
      case .reducedAccuracy:

      default:
          break
}

ここは開発するアプリによって変わってきますので、よく吟味した方が良い部分ですね。
こちらの記事が詳しく書いていますので参考にしてみて下さい↓
iOS14でのCore Location変更点
iOS 14 でさらに強化された位置情報まわりのプライバシー

※今回は、位置情報の取得で配慮する点をまとめた記事なので
あまり正確かどうかは関係ないので紹介だけに留めておきます。

注目してほしい部分は、最初の方で説明した推奨・非推奨のコードです。
iOS14以前のバージョンに対応させるには非推奨のコードも使用しなければいけません。

では早速、実装していきます。

iOS14以前のバージョンに対応した場合の実装

※長いので一部抜粋

ViewController
import UIKit
import CoreLocation

final class ViewController: UIViewController {

    private var locationManager: CLLocationManager = {
        var locationManager = CLLocationManager()
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.distanceFilter = 5
        return locationManager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
    }
}
    // 推奨しているデリゲートメソッド
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        // iOS14以降の場合
        if #available(iOS 14.0, *) {
            if CLLocationManager.locationServicesEnabled() {

                let status = manager.authorizationStatus

                switch status {
                case .authorizedAlways, .authorizedWhenInUse:
                    manager.startUpdatingLocation()

                case .notDetermined:
                    manager.requestWhenInUseAuthorization()

                case .denied:
                    Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                        UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                    }

                case .restricted:
                    Alert.okAlert(vc: self, title: "位置情報サービスの使用を\n許可されていません", message: "何らかの制限が掛かっています")

                default:
                    break
                }
            }else {
                Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
            }
        }
    }
    // 非推奨のデリゲートメソッド
    //iOS14以前の場合
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if CLLocationManager.locationServicesEnabled() {

            switch status {
            case .authorizedAlways, .authorizedWhenInUse:
                manager.startUpdatingLocation()

            case .notDetermined:
                manager.requestWhenInUseAuthorization()

            case .denied:
                Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                }

            case .restricted:
                Alert.okAlert(vc: self, title: "位置情報サービスの使用を\n許可されていません", message: "")

            default:
                break
            }

        }else {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
    }

こんな感じの実装となり、どちらも記述します。
メソッド内は、バージョンの指定以外、ほぼ変わらないですね。

これでiOS14以前のバージョンに対応できました。

エラー処理

どの場面でも大体、エラー処理というものは存在しますが、もちろんCLLocationManagerでも存在します。
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)

では、どのようにViewController内で記述するのかというと、こんな感じです↓

ViewController
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    //本来なら、アラートで画面にエラー内容を表示したりする
    print("\((error as NSError).domain)")
}

このデリゲートメソッドはCLLocationManagerが位置情報を取得できなかった時に呼ばれます。
このメソッドを実装していない場合、エラーをスルーしてしまうので要注意です。

様々なエラーがあるので、見てみて下さい↓
CLError

そして、ここからは自分が苦戦した所を紹介します。

苦戦した部分~認証ステータスの取得~

認証ステータスの取得も、先ほど紹介しましたが推奨・非推奨のコードがありましたね。
ここが、かなり厄介?で現在進行形で悩んでいます...。

// 位置情報の認証ステータス取得

// 推奨していない iOS 4.2–14.0
func authorizationStatus() -> CLAuthorizationStatus

// 推奨している iOS14-
var authorizationStatus: CLAuthorizationStatus

まず、この2つのコードを比較すると
推奨していない方はメソッド、推奨している方はプロパティとなっています。

ここがまず、大きな違いですね。

そして、推奨していないfunc authorizationStatus()メソッドは
アプリがバックグラウンド・フォアグラウンド状態関係なく認証ステータスを返してくれますが

今回、iOS14で新しく登場したauthorizationStatusプロパティの方は
フォアグラウンド状態の時の認証ステータスしか返してくれません。

つまり、authorizationStatusプロパティの方はバックグラウンド状態で認証ステータスを変更した場合
フォアグラウンド状態の時の認証ステータスを返すんですね。

じゃあ、この部分の一体どこで苦戦したのかというと

バックグラウンド状態から戻ってきた時にアプリの位置情報サービスの有無をチェックする部分です。

実際に分かりやすいようにコードで説明しますね。
認証ステータスを取得する場合、今までだと下記の実装でした↓

ViewController
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

   // バックグラウンド状態から戻った時に呼ばれるメソッド
   @objc private func willEnterForeground() {
      if CLLocationManager.locationServicesEnabled() {
         // 認証ステータスの取得(非推奨)
         let status = CLLocationManager.authorizationStatus()

         switch status {

         // 許可しない場合
         case .denied:
             Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                 UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
             }

            default:
                break
            }

        }else {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
      }
   }

では次に、推奨しているコードでの実装↓

ViewController
    private var locationManager: CLLocationManager = {
        var locationManager = CLLocationManager()
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.distanceFilter = 5
        return locationManager
    }()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

   // バックグラウンド状態から戻った時に呼ばれるメソッド
   @objc private func willEnterForeground() {
      if CLLocationManager.locationServicesEnabled() {
         // 認証ステータスの取得(推奨)
         let status = locationManager.authorizationStatus

         switch status {

         // 許可しない場合
         case .denied:
             Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                 UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
             }

            default:
                break
            }

        }else {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
      }
   }

さほど変化はないんですが、これをビルドしてみると違いが分かります。

まず、どちらも起動時に認証ステータスが .denidである場合はちゃんとアラートを表示します。
ですが、バックグラウンド状態で .denid > .restrictedに変更してフォアグラウンド状態に戻すとこうなります↓
ezgif.com-gif-maker4.gif

本来、許可しない(.denid)で表示されるアラートが
Appの使用中は許可(.authorizedWhenInUse)でも表示されてしまいました。

この不具合が、発生してしまうので仕方なく非推奨のfunc authorizationStatus()メソッド
を呼んでいる現状なんですね。

authorizationStatusプロパティはどう使うんだろう?

色々、試行錯誤していきながら調べながら自分はこういう結論に至りました。

そもそも、iOS14で登場したauthorizationStatusプロパティは

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)ありきで使用してこのメソッド内で使っていく。

じゃあ、そもそも非推奨のfunc authorizationStatus()メソッドは使わなくても良いんじゃない?

そのメソッドはCLLocationManagerクラスの初期化及び、バックグラウンド・フォアグラウンド状態関係なく認証ステータスが変更されたら呼ばれるデリゲートメソッドだし、willEnterForeground()は必要ないのでは?↓

// バックグラウンド時に戻った時に呼ばれるメソッド
@objc private func willEnterForeground()

と思う方がいらっしゃると思います。

では、このような場面ではどうでしょうか?

例えば、ユーザーに対して位置情報の許可をリクエストして
アラートが表示され、許可しないを選択したとします。

今までの流れからいくと、このようなアラートが表示されて設定アプリに画面遷移します。

これで、設定アプリに画面遷移したはいいけど何もしないで戻った時
func willEnterForeground()を呼ばない場合どうでしょうか?

呼ばない場合は、何もチェックされません。

もちろん、認証ステータスが変更されていないので
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
も呼ばれません。

以上の理由からfunc willEnterForeground()を使用していますし、
非推奨のfunc authorizationStatus()メソッドも使用している現状なんですね。

この部分の改善方法を色々、調べたんですが最新の記事でも非推奨のfunc authorizationStatus()メソッドを使用していて頭の中は???でいっぱいでした。

この解決方法をご存知の方は、コメントで教えて下さると有り難いです。

おわり

という事で三部構成という長い記事になってしまいましたが、今回で完結です。
位置情報を取得するだけで、ここまで配慮しなくてはいけないというのはびっくりですね。

もしかしたら、ここは全然違うよー!という部分があるかもしれません。
そういう場合は遠慮なく、コメントして下さると有り難いです。

※おわりで言うのもなんですが、アラートのタイトルなどで何度も同じことを書くのは
マジックナンバー扱いになってしまい駄目なのですが、これは後日、記事としてまとめて投稿します。

ここまでのソースコードは下記にて載せておきますので良かったら参考にしてみて下さい↓

最後まで読んで下さり、ありがとうございました。

※追記

extentionファイルを作って拡張すれば、ViewControllerのボリュームを抑えることも出来ます↓

CLLocationManager+
import UIKit
import CoreLocation

extension CLLocationManager {
    func alertStatusIfNeeded(vc: UIViewController) {
        if #available(iOS 14.0, *) {
            if CLLocationManager.locationServicesEnabled() {
                let status = self.authorizationStatus

                switch status {
                case .notDetermined:
                    self.requestWhenInUseAuthorization()

                case .restricted:
                    // アラート表示したり

                case .denied:
                    // アラート表示したり

                case .authorizedAlways:
                    self.startUpdatingLocation()

                case .authorizedWhenInUse:
                    self.startUpdatingLocation()

                default:
                    break
                }
            }else {
                // アラート表示したり
            }
        }
    }

    func alertStatusIfNeededUnderiOSVer(vc: UIViewController) {
        if CLLocationManager.locationServicesEnabled() {
            let status = CLLocationManager.authorizationStatus()

            switch status {
            case .notDetermined:
                self.requestWhenInUseAuthorization()

            case .restricted:
                // アラート表示したり

            case .denied:
                // アラート表示したり

            case .authorizedAlways:
                self.startUpdatingLocation()

            case .authorizedWhenInUse:
                self.startUpdatingLocation()

            default:
                break
            }
        }else {
           // アラート表示したり
    }

    func alertStatusIfNeededBackground(vc: UIViewController) {
        if !CLLocationManager.locationServicesEnabled() {
            // アラート表示したり
            return
        }

        let status = CLLocationManager.authorizationStatus()

        if status == .denied {
            // アラート表示したり
        }
    }
}

ViewControllerではこんな感じになります↓
※一部抜粋

ViewController
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        manager.alertStatusIfNeeded(vc: self)
    }

ソースコード

こちらにもあげてます。

ViewController
import UIKit
import CoreLocation

final class ViewController: UIViewController {

    private var locationManager: CLLocationManager = {
        var locationManager = CLLocationManager()
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.distanceFilter = 5
        return locationManager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    @objc private func willEnterForeground() {
        if CLLocationManager.locationServicesEnabled() {

            let status = CLLocationManager.authorizationStatus()

            switch status {
            case .denied:
                Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                }

            default:
                break
            }

        }else {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
    }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if #available(iOS 14.0, *) {
            if CLLocationManager.locationServicesEnabled() {

                let status = manager.authorizationStatus

                switch status {
                case .authorizedAlways, .authorizedWhenInUse:
                    manager.startUpdatingLocation()

                case .notDetermined:
                    manager.requestWhenInUseAuthorization()

                case .denied:
                    Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                        UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                    }

                case .restricted:
                    Alert.okAlert(vc: self, title: "位置情報サービスの使用を\n許可されていません", message: "")

                default:
                    break
                }
            }else {
                Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
            }
        }
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if CLLocationManager.locationServicesEnabled() {

            switch status {
            case .authorizedAlways, .authorizedWhenInUse:
                manager.startUpdatingLocation()

            case .notDetermined:
                manager.requestWhenInUseAuthorization()

            case .denied:
                Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
                }

            case .restricted:
                Alert.okAlert(vc: self, title: "位置情報サービスの使用を\n許可されていません", message: "")

            default:
                break
            }

        }else {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let gps = manager.location?.coordinate else {
            return
        }
        manager.stopUpdatingLocation()
        let lat = gps.latitude
        let lng = gps.longitude
        print("経度:\(String(describing: lat)), 緯度:\(String(describing: lng))")
    }
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("\((error as NSError).domain)")
    }
}
Alert
import UIKit

final class Alert {
    static func okAlert(vc: UIViewController,title: String, message: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let okAlertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
        okAlertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: handler))
        vc.present(okAlertVC, animated: true, completion: nil)
    }
}
5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7