Baseflow / Baseflow/flutter-permission-handler

[Bug]: PhonePermissionStrategy.checkServiceStatus calls completionHandler twice, causing crashes

Open Beginner friendly
#1,565 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
2.2k
Forks
970
Avg merge
14h 22m
Merged PRs (30d)
2

Description

### Please check the following before submitting a new issue.

- [x] I have searched the [existing issues](https://github.com/baseflow/flutter-permission-handler/issues).
- [x] I have carefully [read the documentation](https://github.com/Baseflow/flutter-permission-handler/blob/main/permission_handler/README.md) and verified I have added the required platform specific configuration.

### Please select affected platform(s)

- [ ] Android
- [x] iOS
- [ ] Windows

### Steps to reproduce

1. Call `Permission.phone.serviceStatus` (or anything that triggers `checkServiceStatus` for the phone permission) on a device/simulator where `[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]]` returns `NO` (e.g. iPad, or a device without telephony capability).
2. In `PhonePermissionStrategy.m`, the `if (![app canOpenURL:telURL])` branch calls `completionHandler(ServiceStatusNotApplicable)` but does not `return`, so execution falls through and calls `completionHandler(...)` a second time with the enabled/disabled status.
3. The Flutter method channel result callback backing `completionHandler` is only expected to be invoked once; the second invocation raises an exception/crash.

We noticed this because `checkServiceStatus`/`PhonePermissionStrategy` shows up as one of the **top crashes in our production consumer app in Firebase Crashlytics**, affecting users on devices where `canOpenURL:` returns `NO` for `tel://`.

### Expected results

`completionHandler` should be invoked exactly once per call to `checkServiceStatus`.

### Actual results

`completionHandler` is invoked twice when the device cannot open `tel://` URLs, which can crash the app.

### Code sample

Code sample

```objc
// permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/strategies/PhonePermissionStrategy.m

- (void)checkServiceStatus:(PermissionGroup)permission completionHandler:(ServiceStatusHandler)completionHandler {
UIApplication *app = [UIApplication sharedApplication];
NSURL *telURL = [NSURL URLWithString:@"tel://"];
if (![app canOpenURL:telURL]) {
completionHandler(ServiceStatusNotApplicable);
// missing `return;` here — falls through and calls completionHandler again below
}
completionHandler([self canDevicePlaceAPhoneCall] ? ServiceStatusEnabled : ServiceStatusDisabled);
}
```

### Screenshots or video

Screenshots or video demonstration

Crashlytics shows this as a top crash in our production app; no visual repro available since it's a native-side double-callback crash.

### Version

9.6.1 (permission_handler_apple)

### Flutter Doctor output

Doctor output

```console
N/A — issue is isolated to the native iOS implementation and reproducible by code inspection, independent of Flutter/toolchain version.
```

Contributor guide

Open the contributing guide

Research direction

Open permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/strategies/PhonePermissionStrategy.m and start at checkServiceStatus:completionHandler:. Inspect the canOpenURL:tel:// branch and the following status check. Done means the completionHandler is invoked exactly once when tel:// cannot be opened, without the iOS method-channel crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
objective-c
Domain
mobile-dev
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.