iOSアプリで「常に」位置アクセスを強制する方法
私が構築しているアプリは、正しく機能するために常に位置情報へのアクセスが必要です。アプリは基本的に場所を追跡し、それを地図などに配置します(詳細は重要ではありません、笑)。
私の目標はこれです:
- 「常に」位置情報アクセスを有効にするようにユーザーに促す
- 常に位置情報へのアクセスが要求されているが、ユーザーが「いいえ」と言った場合は、アプリを使用不可にします。基本的には、設定を変更できる設定にリダイレクトする小さなボタンを表示するだけです。
私のAppDelegate.swiftはCLLocationManagerDelegateを実装しており、コードは次のとおりです。
alreadyRequestedLocationWhenInUse = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationWhenInUse")
alreadyRequestedLocationAlways = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationAlways")
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
if (!alreadyRequestedLocationWhenInUse) {
print("Requesting location access 'while in use'.")
self.locationManager.requestWhenInUseAuthorization();
UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationWhenInUse")
alreadyRequestedLocationWhenInUse = true
} else {
promptToChangeLocationSettings()
}
case .restricted, .denied:
print("No Location access")
promptToChangeLocationSettings()
break;
case .authorizedWhenInUse:
if (!alreadyRequestedLocationAlways) {
print("Requesting location access 'Always'.")
self.locationManager.requestAlwaysAuthorization()
UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationAlways")
alreadyRequestedLocationAlways = true
} else {
promptToChangeLocationSettings()
}
break;
case .authorizedAlways:
self.startLocationMonitoring();
break;
default:
self.locationManager.requestAlwaysAuthorization();
return
}
}
ここで、promptToChangeLocationSettings()は、ユーザーをアプリの設定ページに移動させる、適切に機能する関数です。
問題は、ユーザーがアプリを終了して戻ってくるまで、「常に位置追跡」を有効にするように求められないことです。彼らは「使用中」の許可を求められます(そしてそれが機能する方法は彼らが最初にそれに対してイエスと言わなければならないことを私は知っています)、しかし私は常にプロンプトがすぐに起こることを望みます!理論的には、locationManagerDidChangeAuthorization関数は、「while use」承認が付与された後に再度呼び出される必要がありますが、これは発生しません。なぜこれが起こらないのですか?代わりに、promptUserToChangeLocationSettings()が実行され、ユーザーが「常に」位置アクセスを有効にするかどうかを尋ねる小さなポップアップを取得する前に、アプリを使用できなくなります。
誰かが私がこれを修正するのを手伝ってもらえますか?ちなみに、私はUserDefaultsを使用して、ロケーション許可リクエストを実行したかどうかを追跡しています(リクエストは1回しか実行できないため)。
回答
最初に「使用中」を要求し、それが許可されたときにのみ「常に」を要求するこのフローに関するいくつかの所見(WWDC 2020の場所の新機能で説明):
シミュレータではなく、デバイスでこれを実行するようにしてください。シミュレーターを使用している場合、後続の「使用中」から「常に」への「アップグレード」許可アラートが表示されない場合があります。
この機能はiOS13.4で導入されました。以前のiOS13バージョンでこれを試みていないことを確認してください。これらの以前のバージョンでは、「常に」にアップグレードするための2番目のアラートは表示されません。
requestAlwaysAuthorization
アプリを「暫定的に常に」状態にする可能性のある、コードベースの他の場所に長引くものがないことを確認してください。暫定状態になると、13.0の暫定フローにロックされます。
私はそれがあなたが探しているものではないことを知っていますが、将来の読者のために、上記の代替手段はiOS 13.0で導入されたより単純な「暫定常に」フローです(WWDC 2019のコアロケーションの新機能で概説されています)。呼び出すだけですrequestAlwaysAuthorization
(決して呼び出さないでくださいrequestWhenInUseAuthorization
)。ここでのAppleの目的は、アプリの使用中に「使用中」アラートを表示し、アプリの実行中に位置情報サービスを使用すると「常に」アップグレードアラートを自動的に表示することで、ユーザーが何が起こっているのかをよりよく理解できるようにすることでした。 。
これは、私が望む結果を得たソリューションです。まず、AppDelegate.swift didFinishLaunchingWithOptions関数でlocationManagerDidChangeAuthorization(locationManager)を呼び出します。また、アプリが開くたびに再チェックするように、applicationWillEnterForegroundで呼び出しました。
次に、これは私の新しいlocationManagerDidChangeAuthorization関数です。return / breakステートメントを削除する必要がありますが、忘れる前にこれに答えるつもりです。
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
UserDefaults.standard.set(false, forKey: "alreadyRequestedAlwaysLocationAccess")
alreadyRequestedAlwaysLocationAccess = false
DispatchQueue.main.async{
self.coreLocationManager.requestWhenInUseAuthorization()
self.locationManagerDidChangeAuthorization(manager)
}
break;
case .restricted, .denied:
print("No Location access")
promptToChangeLocationSettings()
break;
case .authorizedWhenInUse:
if (!alreadyRequestedAlwaysLocationAccess) {
print("Requesting location access 'Always'.")
UserDefaults.standard.set(true, forKey: "alreadyRequestedAlwaysLocationAccess")
alreadyRequestedAlwaysLocationAccess = true
DispatchQueue.main.async{
self.coreLocationManager.requestAlwaysAuthorization()
self.locationManagerDidChangeAuthorization(manager)
}
} else {
promptToChangeLocationSettings()
}
break;
case .authorizedAlways:
self.startLocationMonitoring();
break;
default:
return
}
}