迁移至MAUI的iOS应用如何获取蓝牙权限?
MAUI iOS 蓝牙权限请求无法触发弹窗的解决方法
问题背景
将Xamarin.Forms应用迁移至MAUI后,iOS端创建CBCentralManager实例后状态始终为CBManagerState.Unknown,无法触发蓝牙权限请求弹窗,原Xamarin.Forms中的权限请求逻辑失效。原iOS项目已在info.plist中配置了以下权限字符串:
NSBluetoothPeripheralUsageDescription NSBluetoothAlwaysUsageDescription Privacy - Bluetooth Peripheral Usage Description Privacy - Bluetooth Always Usage Description
原权限请求逻辑(更新后):
public async Task<bool> RequestBluetoothPermission() { //iOS 13.0 and below if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 1)) { return true; } //iOS 13.1 and above if (CBManager.Authorization == CBManagerAuthorization.NotDetermined) { TaskCompletionSource<CBCentralManagerState> tcs = new TaskCompletionSource<CBCentralManagerState>(); Action<CBCentralManagerState> handleBluetoothStateChangedForFirstTime = delegate (CBCentralManagerState bluetoothState) { HandleBluetoothStateChanged(bluetoothState); tcs.TrySetResult(bluetoothState); }; var simpleCBCentralManagerDelegate = new SimpleCBCentralManagerDelegate(); simpleCBCentralManagerDelegate.StateChanged += handleBluetoothStateChangedForFirstTime; _cBCentralManager = new CBCentralManager(simpleCBCentralManagerDelegate, null, new CBCentralInitOptions() { ShowPowerAlert = false }); var status = await tcs.Task; simpleCBCentralManagerDelegate.StateChanged -= handleBluetoothStateChangedForFirstTime; simpleCBCentralManagerDelegate.StateChanged += HandleBluetoothStateChanged; return status != CBCentralManagerState.Unauthorized; } // In any else case we can't ask again so we just return the current permission status return CBManager.Authorization == CBManagerAuthorization.AllowedAlways; }
解决步骤
1. 修正info.plist权限配置
MAUI中iOS的权限配置需在Platforms/iOS/Info.plist中添加标准键值对,无需保留带Privacy -前缀的旧写法,同时确保权限描述内容不为空:
<key>NSBluetoothAlwaysUsageDescription</key> <string>需要蓝牙权限连接并管理外设</string> <key>NSBluetoothPeripheralUsageDescription</key> <string>需要蓝牙权限扫描附近外设</string>
注:iOS 13及以上版本主要依赖
NSBluetoothAlwaysUsageDescription,保留旧键是为了兼容更低版本系统。
2. 重构权限请求逻辑
MAUI中需确保CBCentralManager实例被正确持有,且状态回调能被可靠触发,修改后的代码如下:
private CBCentralManager? _centralManager; private TaskCompletionSource<CBManagerAuthorization>? _authTaskSource; public async Task<bool> RequestBluetoothPermission() { // iOS 13.0及以下无需主动请求权限 if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 1)) { return true; } var currentAuth = CBManager.Authorization; // 已授权或拒绝,直接返回当前状态 if (currentAuth != CBManagerAuthorization.NotDetermined) { return currentAuth == CBManagerAuthorization.AllowedAlways; } _authTaskSource = new TaskCompletionSource<CBManagerAuthorization>(); var authDelegate = new BluetoothAuthDelegate(); authDelegate.OnAuthorizationUpdated += HandleAuthUpdate; // 初始化CBCentralManager,绑定主线程队列避免回调丢失 _centralManager = new CBCentralManager(authDelegate, DispatchQueue.MainQueue, new CBCentralInitOptions { ShowPowerAlert = false }); // 等待权限请求结果 var finalAuth = await _authTaskSource.Task; authDelegate.OnAuthorizationUpdated -= HandleAuthUpdate; return finalAuth == CBManagerAuthorization.AllowedAlways; } private void HandleAuthUpdate(CBManagerAuthorization status) { _authTaskSource?.TrySetResult(status); } // 自定义Delegate监听权限状态 private class BluetoothAuthDelegate : CBCentralManagerDelegate { public event Action<CBManagerAuthorization>? OnAuthorizationUpdated; public override void UpdatedState(CBCentralManager central) { OnAuthorizationUpdated?.Invoke(CBManager.Authorization); } }
3. 核心注意事项
- 持有
CBCentralManager实例:必须在类级别声明变量(如_centralManager)持有实例,避免被垃圾回收导致回调不触发。 - 指定有效队列:初始化时绑定
DispatchQueue.MainQueue或其他有效队列,防止因队列问题丢失状态回调。 - 监听
UpdatedState方法:iOS蓝牙权限状态变化会通过该方法通知,需在此处获取最新授权状态。 - 避免重复请求:仅当
CBManager.Authorization为NotDetermined时发起请求,否则直接返回当前状态。
内容的提问来源于stack exchange,提问作者o_w
相关产品推荐
相关产品推荐

