警告メッセージ「This method can cause UI unresponsiveness if invoked on the main thread.」が出たらどうする?

  • 2022年12月2日
  • 2023年11月9日
  • Swift
  • 2375View
  • 0件

シミュレーターをiOS16で動かすと、位置情報の許可ダイアログを表示するプログラムで次のような警告が発生しました。

This method can cause UI unresponsiveness if invoked on the main thread. Instead, consider waiting for the `-locationManagerDidChangeAuthorization:` callback and checking `authorizationStatus` first.

対象箇所のソースはこんな感じです。(抜粋しています。)

class LocationClient: NSObject, ObservableObject, CLLocationManagerDelegate {
    
    var appState: AppState?
    @Published var authorizationStatus: CLAuthorizationStatus
    private let locationManager: CLLocationManager
    private var nowDate: Date?

    convenience init(appState: AppState) {
        self.init()
        self.appState = appState
    }
    
    override init() {

        locationManager = CLLocationManager()
        authorizationStatus = locationManager.authorizationStatus
        nowDate = LogDate.all().last?.date
        
        super.init()

        // 位置情報サービスをサポートしていない端末(GPSを搭載していない端末)の場合はなにもしない
        if !CLLocationManager.locationServicesEnabled() {
            print("No location service")
        } else {
                
            self.locationManager.delegate = self
            self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
            self.locationManager.distanceFilter = 10
            self.locationManager.allowsBackgroundLocationUpdates = true //バックグラウンド処理を可能にする
            self.locationManager.pausesLocationUpdatesAutomatically = false //ポーズしても位置取得を続ける
            self.locationManager.startUpdatingLocation()
                
            print("\(self.locationManager.authorizationStatus.description)")
            if self.locationManager.authorizationStatus == .authorizedAlways {
                self.locationManager.startUpdatingLocation()
            } else {
                //ユーザーが位置情報の許可をまだしていないので、位置情報許可のダイアログを表示する
                self.requestPermission()
            }
                
        }

    }
}

警告文を翻訳すると・・・

このメソッドをメインスレッドで呼び出すと、UI が応答しなくなることがあります。代わりに、locationManagerDidChangeAuthorization: コールバックを待ち、最初に authorizationStatus をチェックすることを検討してください。

DeepL

メインスレッド、つまり直列で実行するなということですね。
では、並列で実行させてみましょう。
CLLocationManagerの設定処理をDispatchQueue.global().asyncで囲いました。

class LocationClient: NSObject, ObservableObject, CLLocationManagerDelegate {
    
    var appState: AppState?
    @Published var authorizationStatus: CLAuthorizationStatus
    private let locationManager: CLLocationManager
    private var nowDate: Date?

    convenience init(appState: AppState) {
        self.init()
        self.appState = appState
    }
    
    override init() {

        locationManager = CLLocationManager()
        authorizationStatus = locationManager.authorizationStatus
        nowDate = LogDate.all().last?.date
        
        super.init()

        DispatchQueue.global().async {  // ←【追加】
            // 位置情報サービスをサポートしていない端末(GPSを搭載していない端末)の場合はなにもしない
            if !CLLocationManager.locationServicesEnabled() {
                print("No location service")
            } else {
                
                self.locationManager.delegate = self
                self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
                self.locationManager.distanceFilter = 10
                self.locationManager.allowsBackgroundLocationUpdates = true //バックグラウンド処理を可能にする
                self.locationManager.pausesLocationUpdatesAutomatically = false //ポーズしても位置取得を続ける
                self.locationManager.startUpdatingLocation()
                
                print("\(self.locationManager.authorizationStatus.description)")
                if self.locationManager.authorizationStatus == .authorizedAlways {
                    self.locationManager.startUpdatingLocation()
                } else {
                    //ユーザーが位置情報の許可をまだしていないので、位置情報許可のダイアログを表示する
                    self.requestPermission()
                }
                
            }
        }

    }
}

無事、警告が出なくなりました。