Fix iOS beacon permissions for location and Bluetooth

iOS Permissions:
- Add missing Info.plist keys for location and Bluetooth usage descriptions
- Configure Podfile to enable PERMISSION_LOCATION and PERMISSION_BLUETOOTH
- Fix beacon_permissions.dart to use correct iOS permission (Permission.bluetooth)
  instead of Android-only bluetoothScan/bluetoothConnect

The permission_handler plugin requires platform-specific handling:
- iOS: Uses Permission.bluetooth for Bluetooth access
- Android: Uses Permission.bluetoothScan and Permission.bluetoothConnect

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-01-08 15:05:11 -08:00
parent 29646a8a04
commit ef5609ba57
6 changed files with 51 additions and 18 deletions

View file

@ -39,5 +39,14 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# Enable permissions for permission_handler
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_LOCATION=1',
'PERMISSION_BLUETOOTH=1',
]
end
end
end

View file

@ -108,6 +108,6 @@ SPEC CHECKSUMS:
StripePaymentsUI: 326376e23caa369d1f58041bdb858c89c2b17ed4
StripeUICore: 17a4f3adb81ae05ab885e1b353022a430176eab1
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
PODFILE CHECKSUM: 585f1361913628fd422da91acb10f1ea62e8189d
COCOAPODS: 1.16.2

View file

@ -97,7 +97,6 @@
F2C711915F4DC721B46B55FB /* Pods-RunnerTests.release.xcconfig */,
BF07229F38A392371BF2BB5F /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@ -490,6 +489,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2UD2H98KPK;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -674,6 +674,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2UD2H98KPK;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -696,6 +697,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2UD2H98KPK;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (

View file

@ -52,7 +52,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"

View file

@ -41,6 +41,14 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Payfrit needs your location to find nearby restaurant tables and beacons.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Payfrit needs your location to find nearby restaurant tables and beacons.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Payfrit uses Bluetooth to detect table beacons for seamless ordering.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Payfrit uses Bluetooth to detect table beacons for seamless ordering.</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>

View file

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
@ -6,22 +7,29 @@ class BeaconPermissions {
try {
// Request location permission (required for Bluetooth scanning)
final locationStatus = await Permission.locationWhenInUse.request();
debugPrint('[BeaconPermissions] Location: $locationStatus');
// Request Bluetooth permissions (Android 12+)
bool bluetoothGranted = true;
if (Platform.isIOS) {
// iOS uses a single Bluetooth permission
final bluetoothStatus = await Permission.bluetooth.request();
debugPrint('[BeaconPermissions] Bluetooth (iOS): $bluetoothStatus');
bluetoothGranted = bluetoothStatus.isGranted;
} else {
// Android 12+ requires separate scan/connect permissions
final bluetoothScan = await Permission.bluetoothScan.request();
final bluetoothConnect = await Permission.bluetoothConnect.request();
debugPrint('[BeaconPermissions] BluetoothScan: $bluetoothScan, BluetoothConnect: $bluetoothConnect');
bluetoothGranted = bluetoothScan.isGranted && bluetoothConnect.isGranted;
}
final allGranted = locationStatus.isGranted &&
bluetoothScan.isGranted &&
bluetoothConnect.isGranted;
final allGranted = locationStatus.isGranted && bluetoothGranted;
if (allGranted) {
debugPrint('[BeaconPermissions] ✅ All permissions granted');
} else {
debugPrint('[BeaconPermissions] ❌ Permissions denied: '
'location=$locationStatus, '
'bluetoothScan=$bluetoothScan, '
'bluetoothConnect=$bluetoothConnect');
debugPrint('[BeaconPermissions] ❌ Permissions denied');
}
return allGranted;
@ -33,12 +41,18 @@ class BeaconPermissions {
static Future<bool> checkPermissions() async {
final locationStatus = await Permission.locationWhenInUse.status;
bool bluetoothGranted = true;
if (Platform.isIOS) {
final bluetoothStatus = await Permission.bluetooth.status;
bluetoothGranted = bluetoothStatus.isGranted;
} else {
final bluetoothScan = await Permission.bluetoothScan.status;
final bluetoothConnect = await Permission.bluetoothConnect.status;
bluetoothGranted = bluetoothScan.isGranted && bluetoothConnect.isGranted;
}
return locationStatus.isGranted &&
bluetoothScan.isGranted &&
bluetoothConnect.isGranted;
return locationStatus.isGranted && bluetoothGranted;
}
static Future<void> openSettings() async {