bug(geolocation): iOS requestPermissions never settles after "Allow Once" lapses, and the plugin prompts unasked

オープン
#2,624 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
ios, swift
領域
mobile

調査の方向性

Start in GeolocationPlugin.swift: read requestPermissions, bindAuthorisationStatusPublisher (the .notDetermined sink), and handleLocationRequest, along with the callback manager's requestPermissionsCallbacks, locationCallbacks and watchCallbacks. Reproduce on an iOS simulator by choosing "Allow Once", backgrounding the app, then calling requestPermissions again. Done means that call resolves or shows a prompt when the status is .notDetermined, and that no system prompt appears without a pending JS call.

索引モデルが issue の本文から書いたものです。

説明

platform: ios

Bug Report

Plugin(s)

@capacitor/geolocation 8.2.4 (iOS). The same code is in 9.0.0-next.3.

Capacitor Version

@capacitor/core and @capacitor/ios 8.5.3; iPhone 16 Pro simulator, iOS 18.3.

Platform(s)

iOS

Current Behavior

Two problems, both from how GeolocationPlugin.swift handles the .notDetermined status.

1. requestPermissions() (and getCurrentPosition()) never settle after "Allow Once" lapses.

  1. Call requestPermissions() and choose Allow Once, then getCurrentPosition(): works.
  2. Send the app to the background (for example, open Apple Maps). About 10 seconds later iOS ends the one-time grant and the status becomes .notDetermined.
  3. The plugin's authorisationStatusPublisher sink sees .notDetermined and calls requestLocationAuthorisation by itself. The app is not in use, so locationd drops it:
    locationd: {"msg":"#AuthPrompt #Warning Dropping authorization request placed while insufficiently in-use", "ClientKeyPath":"i<bundle id>:", "requestType":2, "inUseLevel":0}
    
  4. Back in the app, call checkPermissions() (returns prompt), then requestPermissions(). Because the status is already .notDetermined, requestPermissions only adds the call to requestPermissionsCallbacks and waits for the publisher. The publisher never fires again, since nothing changes, and no new authorisation request is made. The promise never resolves or rejects. getCurrentPosition parks the same way (default: break in handleLocationRequest), and its timeout does not help because the timer only starts in requestSingleLocation, after authorisation.

Every later call hangs too, until the app process is restarted.

2. A permission prompt appears without any call.

Once any plugin method has been called in the process, the sink stays subscribed. Whenever the status changes to .notDetermined while the app is active, the sink requests authorisation, and the system prompt appears over whatever screen is open, with no JS call behind it:

locationd: {"msg":"Showing #AuthPrompt", "requestType":2, "Client":"i<bundle id>:"}
Expected Behavior
  • requestPermissions() shows the prompt (or resolves) whenever the status is .notDetermined at the time of the call.
  • The plugin only asks for authorisation on behalf of a pending call.
Code Reproduction
await Geolocation.requestPermissions({ permissions: ['coarseLocation'] }); // choose "Allow Once"
await Geolocation.getCurrentPosition();                                    // works
// background the app for ~10 s, return
await Geolocation.checkPermissions();                                      // { coarseLocation: 'prompt' }
await Geolocation.requestPermissions({ permissions: ['coarseLocation'] }); // never settles
Additional Context

The patch we ship (patch-package), which fixes both cases in our testing:

@@ requestPermissions
         if locationService?.authorisationStatus == .notDetermined {
             shouldSetupBindings()
             callbackManager?.addRequestPermissionsCallback(capacitorCall: call)
+            requestLocationAuthorisation(type: .whenInUse)
         } else {
@@ bindAuthorisationStatusPublisher
                 case .notDetermined:
-                    self.requestLocationAuthorisation(type: .whenInUse)
+                    if self.hasPendingCallbacks() {
+                        self.requestLocationAuthorisation(type: .whenInUse)
+                    }
@@ handleLocationRequest
         case .restricted: callbackManager?.sendError(.permissionRestricted)
+        case .notDetermined: requestLocationAuthorisation(type: .whenInUse)
         default: break
@@
+    func hasPendingCallbacks() -> Bool {
+        guard let callbackManager else { return false }
+        return !callbackManager.requestPermissionsCallbacks.isEmpty
+            || !callbackManager.locationCallbacks.isEmpty
+            || !callbackManager.watchCallbacks.isEmpty
+    }
主要言語
Java
スター
688
フォーク
691
平均マージ
1日 15時間
マージ済み PR(30日)
3

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

ionic-team/capacitor-plugins のほかの issue

ionic-team/capacitor-plugins の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。