Platform Setup Guide
This guide covers platform-specific configuration required for Plugin.Bluetooth to work on iOS, Android, and Windows.
Overview
Each platform requires specific permissions and capabilities to access Bluetooth hardware:
- iOS/MacCatalyst: Usage description strings in
Info.plist - Android: Runtime permissions in
AndroidManifest.xml(varies by API level) - Windows: Device capabilities in
Package.appxmanifest
Plugin.Bluetooth automatically handles runtime permission requests by default. This guide focuses on the required manifest/configuration file entries.
iOS & MacCatalyst
Required Configuration
Add Bluetooth usage descriptions to your Info.plist file located at:
Platforms/iOS/Info.plistPlatforms/MacCatalyst/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Other keys... -->
<!-- Required: Bluetooth Always Usage Description -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to nearby devices</string>
<!-- Legacy: Still recommended for older iOS versions -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to connect to nearby devices</string>
</dict>
</plist>
Usage Description Keys
| Key | iOS Version | Purpose | Required |
|---|---|---|---|
NSBluetoothAlwaysUsageDescription |
iOS 13+ | Main Bluetooth permission | ✅ Yes |
NSBluetoothPeripheralUsageDescription |
iOS 6-12 | Legacy Bluetooth permission | ⚠️ Recommended |
Customizing Permission Messages
Provide clear, user-friendly descriptions explaining why your app needs Bluetooth:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We use Bluetooth to connect to your heart rate monitor and track your fitness activities</string>
Important: Apps will crash at runtime if you attempt to use Bluetooth without these entries in
Info.plist.
Background Modes (Optional)
If your app needs to scan or communicate with Bluetooth devices while in the background, add background mode capabilities:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<!-- Add bluetooth-peripheral if acting as a peripheral -->
</array>
Platform Behavior
- Permission Dialog: Shown automatically by iOS when first accessing Bluetooth (CoreBluetooth)
- Auto-Request: Plugin.Bluetooth's
RequestAutomaticallystrategy is effectively a no-op on iOS - Re-Authorization: Users must go to Settings to re-enable if denied; subsequent requests show "Go to Settings" dialog
Android
Android Bluetooth permissions have evolved significantly across API levels. The required permissions depend on your target Android version.
Permission Evolution Summary
| API Level | Android Version | Required Permissions |
|---|---|---|
| API 23-28 | Android 6-8.1 | BLUETOOTH, BLUETOOTH_ADMIN, ACCESS_FINE_LOCATION |
| API 29-30 | Android 10-11 | Above + ACCESS_BACKGROUND_LOCATION (for background) |
| API 31+ | Android 12+ | BLUETOOTH_SCAN, BLUETOOTH_CONNECT (no location needed) |
Basic Configuration (Targets API 31+)
For modern apps targeting Android 12 and above:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Bluetooth permissions for Android 12+ (API 31+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Optional: For broadcasting (peripheral mode) -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<application>
<!-- Your app configuration -->
</application>
</manifest>
Note: The
android:usesPermissionFlags="neverForLocation"attribute onBLUETOOTH_SCANtells the system you don't need location data, simplifying the permission request for users.
Backwards Compatible Configuration
For apps supporting older Android versions (API 23-30):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Legacy Bluetooth permissions (API 23-30) -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<!-- Optional: Background location for scanning when app is in background (API 29-30) -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
android:maxSdkVersion="30" />
<!-- Modern Bluetooth permissions (API 31+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Optional: For broadcasting (peripheral mode) -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<application>
<!-- Your app configuration -->
</application>
</manifest>
Detailed Permission Breakdown
API 23-28 (Android 6.0 - 8.1)
<!-- Basic Bluetooth access -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Required: Location permission for BLE scanning -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Alternative: Coarse location (less precise but sufficient for scanning) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Why location? On Android 6-11, BLE scanning requires location permission because device MAC addresses can be used to infer location.
API 29-30 (Android 10-11)
All permissions from API 23-28, plus:
<!-- For scanning when app is in background -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
API 31+ (Android 12+)
<!-- Scan for nearby Bluetooth devices -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<!-- Connect to paired/discovered devices -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Optional: Advertise as a peripheral device -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
Background Scanning Permissions
If your app needs to scan for devices while in the background:
API 29-30
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Request this permission through ScanningOptions:
var options = new ScanningOptions
{
RequireBackgroundLocation = true
};
await scanner.StartScanningAsync(options);
API 31+
Remove neverForLocation flag and request location permission:
<!-- Note: No neverForLocation flag -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Hardware Features
Declare Bluetooth as a required or optional hardware feature:
<!-- Optional: App works without Bluetooth -->
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="false" />
<!-- Required: App requires Bluetooth to function -->
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true" />
Setting android:required="false" allows your app to be installed on devices without Bluetooth but requires runtime checks. Setting true prevents installation on incompatible devices.
Platform Behavior
- Runtime Requests: On API 23+, Plugin.Bluetooth requests permissions at runtime automatically
- Permission Dialogs: Users see platform permission dialogs explaining what's being requested
- Retry Handling: If denied, subsequent requests show "Don't ask again" checkbox
- Settings Override: Users can grant/revoke permissions in Settings at any time
Common Configuration Example
Complete AndroidManifest.xml for most apps:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Declare app requires Bluetooth LE -->
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="false" />
<!-- Legacy permissions (API 23-30) -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<!-- Modern permissions (API 31+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:allowBackup="true"
android:icon="@mipmap/appicon"
android:supportsRtl="true">
<!-- Your activities -->
</application>
</manifest>
Windows
Required Configuration
Add the Bluetooth device capability to your Package.appxmanifest file located at Platforms/Windows/Package.appxmanifest:
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<!-- Package identity and properties -->
<Identity Name="YourApp" Publisher="CN=YourCompany" Version="1.0.0.0" />
<!-- Other configuration... -->
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<!-- Required: Bluetooth device capability -->
<DeviceCapability Name="bluetooth" />
</Capabilities>
</Package>
Capability Details
| Capability | Purpose | Required |
|---|---|---|
bluetooth |
Access to Bluetooth LE APIs | ✅ Yes |
runFullTrust |
Desktop app capability | ✅ Yes (for MAUI) |
Platform Behavior
- No Runtime Dialog: Windows doesn't show a permission dialog for Bluetooth access
- Adapter State: Plugin.Bluetooth checks if Bluetooth adapter is available and powered on
- Radio Access: Automatically requests radio access if adapter is off
- Settings Link: If Bluetooth is disabled, users must enable it in Windows Settings
Bluetooth Adapter Requirements
Your Windows device must have:
- Bluetooth 4.0+ adapter (for BLE support)
- Bluetooth drivers installed and enabled
- Bluetooth radio turned on
Checking Adapter Availability
var hasPermission = await scanner.HasScannerPermissionsAsync();
if (!hasPermission)
{
// On Windows, this means adapter is not available or turned off
// Guide user to turn on Bluetooth in Windows Settings
}
Permission Request Strategies
Plugin.Bluetooth provides three strategies for handling permissions across all platforms:
RequestAutomatically (Default)
Permissions are requested automatically when starting operations:
// Default behavior - permissions requested automatically
await scanner.StartScanningAsync();
Platform Behavior:
- Android: Shows permission dialog before scanning starts
- iOS: No-op (CoreBluetooth shows dialog automatically)
- Windows: Checks adapter state and requests radio access
ThrowIfNotGranted
Take explicit control over when permissions are requested:
// Check permissions first
if (!await scanner.HasScannerPermissionsAsync())
{
try
{
// Request permissions explicitly
await scanner.RequestScannerPermissionsAsync();
}
catch (BluetoothPermissionException ex)
{
// Handle permission denial
ShowMessage("Bluetooth permission is required to scan for devices");
return;
}
}
// Now start scanning with explicit strategy
var options = new ScanningOptions
{
PermissionStrategy = PermissionRequestStrategy.ThrowIfNotGranted
};
await scanner.StartScanningAsync(options);
AssumeGranted
Skip all permission checks (use with caution):
var options = new ScanningOptions
{
PermissionStrategy = PermissionRequestStrategy.AssumeGranted
};
await scanner.StartScanningAsync(options);
Warning: If permissions are not actually granted, this will cause runtime errors:
- Android:
SecurityExceptionfrom native code- iOS: Permission dialog will still appear (cannot be prevented)
- Windows: May fail when accessing Bluetooth APIs
Use AssumeGranted only when you've already requested and verified permissions through another mechanism.
Testing Permission Handling
iOS Simulator
- Bluetooth is not available on iOS Simulator
- Test on physical devices only
Android Emulator
- Bluetooth is typically not functional on emulators
- Test on physical devices for reliable results
- Some emulators support Bluetooth passthrough but with limitations
Windows
- Requires physical Bluetooth adapter
- Can test on development machines with Bluetooth
Troubleshooting
iOS: App crashes when accessing Bluetooth
Cause: Missing NSBluetoothAlwaysUsageDescription in Info.plist
Solution: Add the required key with a description string to your Info.plist
Android: "Permission denied" on devices running Android 12+
Cause: Missing BLUETOOTH_SCAN or BLUETOOTH_CONNECT permissions
Solution: Add Android 12+ permissions to AndroidManifest.xml:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Android: Devices not discovered on Android 6-11
Cause: Missing location permission or location services disabled
Solution:
- Add location permission to
AndroidManifest.xml - Ensure location services are enabled in device settings
- Request location permission at runtime
Windows: Cannot find any Bluetooth devices
Cause: Bluetooth capability not declared or Bluetooth adapter is off
Solution:
- Add
<DeviceCapability Name="bluetooth" />toPackage.appxmanifest - Ensure Bluetooth is enabled in Windows Settings
- Check that Bluetooth adapter drivers are installed
Windows: "Bluetooth adapter not available"
Cause: No Bluetooth hardware or drivers not installed
Solution: Verify device has Bluetooth 4.0+ hardware and drivers are properly installed
Platform-Specific Notes
iOS/MacCatalyst
- Permission dialog is shown automatically by CoreBluetooth
- Users cannot re-grant permission from within app; must use Settings
- Background scanning requires additional background mode configuration
- Mac Catalyst apps have same requirements as iOS
Android
- Permission model varies drastically across API levels
- Location services must be enabled on device for API 23-30
- "Don't ask again" can permanently block permission requests
- Some manufacturers (Samsung, Xiaomi) have additional permission layers
Windows
- No runtime permission dialog
- Adapter must be available and enabled
- Desktop apps (MAUI) require
runFullTrustcapability - UWP apps have different manifest schema (not applicable to MAUI)
Next Steps
Now that you've configured platform permissions, continue with:
- Getting Started Guide - Basic usage and examples
- Permissions Guide - Detailed permission handling strategies
- Architecture Guidelines - Understand the library design