Bluetooth Facade Pattern Implementation Summary
Date: March 20, 2026 Status: ✅ Scanner & Broadcaster facades complete Architecture: Wrapper/Facade Pattern with Platform Abstraction
Overview
Successfully implemented unified cross-platform facade wrappers for IBluetoothScanner and IBluetoothBroadcaster that enable client projects to inherit from a single class across all platforms (Android, iOS/macOS, Windows, DotNetCore) without dealing with #if directives.
Completed Work
✅ BluetoothScanner.cs (Bluetooth.Maui)
Location: /Bluetooth.Maui/BluetoothScanner.cs
Pattern: Facade wrapping platform-specific scanners
Lines of Code: ~450
Key Features:
Platform Abstraction: Creates platform-specific scanner via conditional compilation:
AndroidBluetoothScanner(Android)AppleBluetoothScanner(iOS/macOS)WindowsBluetoothScanner(Windows)DotNetCoreBluetoothScanner(Fallback)
Full Interface Delegation: Implements complete
IBluetoothScannerinterface (~30 methods):- Properties:
IsRunning,IsStarting,IsStopping,AdvertisementFilter - Events:
AdvertisementReceived,RunningStateChanged,Starting/Started,Stopping/Stopped, device list events - Methods: Scanning control, permissions, device management (Get/Has/Clear/Wait)
- Properties:
Virtual Extension Points:
protected virtual void OnAdvertisementReceived(IBluetoothAdvertisement advertisement) protected virtual ValueTask OnScanStartingAsync(ScanningOptions? options, CancellationToken ct) protected virtual ValueTask OnScanStoppedAsync(CancellationToken ct)
Construction Pattern:
- Android/Windows/DotNetCore:
(adapter, rssiConverter, ticker, nameProvider?, loggerFactory?) - iOS/macOS:
(adapter, rssiConverter, ticker, cbCentralInitOptions, dispatchQueueProvider, nameProvider?, loggerFactory?)
Client Usage Example:
public class MyCustomScanner : BluetoothScanner
{
public MyCustomScanner(...) : base(...) { }
protected override void OnAdvertisementReceived(IBluetoothAdvertisement advertisement)
{
if (advertisement.DeviceName?.StartsWith("MyDevice") == true)
{
// Custom filtering logic
}
base.OnAdvertisementReceived(advertisement);
}
}
✅ BluetoothBroadcaster.cs (Bluetooth.Maui)
Location: /Bluetooth.Maui/BluetoothBroadcaster.cs
Pattern: Facade wrapping platform-specific broadcasters
Lines of Code: ~500
Key Features:
Platform Abstraction: Creates platform-specific broadcaster via conditional compilation:
AndroidBluetoothBroadcaster(Android)AppleBluetoothBroadcaster(iOS/macOS)WindowsBluetoothBroadcaster(Windows)DotNetCoreBluetoothBroadcaster(Fallback)
Full Interface Delegation: Implements complete
IBluetoothBroadcasterinterface:- Properties:
Adapter,LoggerFactory,CurrentBroadcastingOptions,IsRunning/IsStarting/IsStopping - Events: Broadcast lifecycle, service list, client device connection events
- Methods: Permissions, broadcasting control, service management (Create/Get/Remove), client device management
- Properties:
Virtual Extension Points:
protected virtual ValueTask OnBroadcastStartingAsync(BroadcastingOptions? options, CancellationToken ct) protected virtual ValueTask OnBroadcastStoppedAsync(CancellationToken ct) protected virtual void OnClientDevicesConnected(IReadOnlyList<IBluetoothConnectedDevice> devices) protected virtual void OnClientDevicesDisconnected(IReadOnlyList<IBluetoothConnectedDevice> devices) protected virtual void OnClientDeviceListChanged(ClientDeviceListChangedEventArgs eventArgs)
Construction Pattern:
- Android/Windows/DotNetCore:
(adapter, ticker, loggerFactory?) - iOS/macOS:
(adapter, ticker, cbPeripheralManagerOptions, dispatchQueueProvider, loggerFactory?)
Namespace Disambiguation:
- Qualified ambiguous types with full namespaces:
Abstractions.Broadcasting.EventArgs.ServiceListChangedEventArgsAbstractions.Broadcasting.Options.PermissionOptionsPlatforms.Apple.Broadcasting.NativeObjects.CbPeripheralManagerOptions
✅ ServiceCollectionExtensions.cs (Bluetooth.Maui)
Location: /Bluetooth.Maui/ServiceCollectionExtensions.cs
Changes: Added facade registrations to DI container
Updated Registration:
public static void AddBluetoothServices(this IServiceCollection services)
{
services.AddSingleton<ITicker, Ticker>();
services.AddBluetoothCoreServices();
services.AddBluetoothCoreScanningServices();
services.AddBluetoothCoreBroadcastingServices();
// Platform-specific services
#if WINDOWS
services.AddBluetoothMauiWindowsServices();
#elif ANDROID
services.AddBluetoothMauiAndroidServices();
#elif IOS || MACCATALYST
services.AddBluetoothMauiAppleServices();
#else
services.AddBluetoothMauiDotNetServices();
#endif
// NEW: Register unified facade wrappers as default implementations
services.AddSingleton<IBluetoothScanner, BluetoothScanner>();
services.AddSingleton<IBluetoothBroadcaster, BluetoothBroadcaster>();
}
Effect: Facade registrations override platform-specific registrations, making the facades the default IBluetoothScanner/IBluetoothBroadcaster implementations resolved from DI.
Platform Standardization (Prerequisite Work)
Before implementing facades, all platform implementations were standardized to consistent constructor signatures:
Scanner Constructor Signature:
public <Platform>BluetoothScanner(
IBluetoothAdapter adapter,
IBluetoothRssiToSignalStrengthConverter rssiConverter,
ITicker ticker,
// Platform-specific parameters here (iOS/macOS only)
IBluetoothNameProvider? nameProvider = null,
ILoggerFactory? loggerFactory = null)
Broadcaster Constructor Signature:
public <Platform>BluetoothBroadcaster(
IBluetoothAdapter adapter,
ITicker ticker,
// Platform-specific parameters here (iOS/macOS only)
ILoggerFactory? loggerFactory = null)
Fixed Files:
- ✅
Bluetooth.Maui.Platforms.Apple/Scanning/AppleBluetoothScanner.cs - ✅
Bluetooth.Maui.Platforms.Apple/Broadcasting/AppleBluetoothBroadcaster.cs - ✅
Bluetooth.Maui.Platforms.Win/Scanning/WindowsBluetoothScanner.cs - ✅
Bluetooth.Maui.Platforms.Droid/Scanning/AndroidBluetoothScanner.cs
Key Fixes:
- Reordered parameters to match
BaseBluetoothScanner/BaseBluetoothBroadcaster - Fixed circular dependencies (Apple): Wrappers (
CbCentralManagerWrapper,CbPeripheralManagerWrapper) now created internally by platform classes instead of injected via DI - Removed wrapper DI registrations from platform-specific
ServiceCollectionExtensions - Added missing parameters (Windows scanner -
IBluetoothNameProvider)
Architecture Patterns
Composition Over Inheritance:
- Facades implement
IBluetoothScanner/IBluetoothBroadcasterdirectly - Facades compose platform implementations via private field
_platformScanner/_platformBroadcaster - Delegates all interface members to platform instance
Conditional Compilation Strategy:
#if __ANDROID__
private readonly AndroidBluetoothScanner _platformScanner;
#elif __IOS__ || __MACCATALYST__
private readonly AppleBluetoothScanner _platformScanner;
#elif WINDOWS
private readonly WindowsBluetoothScanner _platformScanner;
#else
private readonly DotNetCoreBluetoothScanner _platformScanner;
#endif
Event Forwarding:
_platformScanner.AdvertisementReceived += (s, e) =>
{
OnAdvertisementReceived(e.Advertisement); // Extension point
AdvertisementReceived?.Invoke(this, e); // Forward to clients
};
PlatformScanner Property:
public IBluetoothScanner PlatformScanner => _platformScanner;
Allows clients to access platform-specific APIs using conditional compilation when needed.
Design Notes
For Client Projects:
- Single Inheritance Point: Inherit
BluetoothScanner/BluetoothBroadcasteronce, works on all platforms - No Platform Knowledge Required: Clients don't need
#ifdirectives for basic customization - Virtual Extension Points: Override specific behaviors without reimplementing entire interface
- Type Safety: Full IntelliSense and compile-time checking across all platforms
For Maintainers:
- Centralized Logic: Common cross-platform behavior in one place
- Platform Flexibility: Platform-specific implementations remain independent
- Layer Separation: Facade layer clearly separated from platform layer
- DI Integration: Standard dependency injection patterns maintained
Testing Verification
✅ Compilation Checks:
BluetoothScanner.cs: ✅ No errorsBluetoothBroadcaster.cs: ✅ No errorsServiceCollectionExtensions.cs: ✅ No errors
Platform Constructor Compatibility:
- Android: ✅
AndroidBluetoothScanner(adapter, rssiConverter, ticker, nameProvider?, loggerFactory?) - iOS/macOS: ✅
AppleBluetoothScanner(adapter, rssiConverter, ticker, cbCentralInitOptions, dispatchQueueProvider, nameProvider?, loggerFactory?) - Windows: ✅
WindowsBluetoothScanner(adapter, rssiConverter, ticker, nameProvider?, loggerFactory?) - DotNetCore: ✅
DotNetCoreBluetoothScanner(adapter, rssiConverter, ticker, nameProvider?, loggerFactory?)
Interface Coverage:
- IBluetoothScanner: ✅ 100% (30+ members)
- IBluetoothBroadcaster: ✅ 100% (25+ members)
Next Steps
Remaining Facade Implementations:
- BluetoothRemoteDevice - Device discovered during scanning
- BluetoothRemoteService - GATT service on remote device
- BluetoothRemoteCharacteristic - GATT characteristic on remote service
- BluetoothRemoteDescriptor - GATT descriptor on remote characteristic
- BluetoothConnectedDevice - Device connected to broadcaster
- BluetoothLocalService - GATT service hosted by broadcaster
- BluetoothLocalCharacteristic - GATT characteristic in local service
- BluetoothLocalDescriptor - GATT descriptor in local characteristic
Estimated Effort:
- Simple classes (Descriptor): ~2 hours each
- Medium classes (Characteristic): ~4 hours each
- Complex classes (Device, Service): ~6 hours each
- Total: ~40 hours for all device/service/characteristic facades
Testing Requirements:
- Platform-specific builds (Android, iOS, Windows)
- Integration tests with real Bluetooth devices
- Verify DI resolution works correctly
- Ensure client inheritance scenarios work
Technical Debt Notes
Current Limitations:
- Dual DI Registration: Platform implementations still registered by platform-specific extensions, then overridden by facade registrations. Consider removing platform registrations or using keyed services.
- Direct Instantiation: Facades create platform instances directly instead of via DI, bypassing potential DI customization hooks.
- XML Documentation: Conditional compilation in XML docs required workaround (documented all params including platform-specific ones).
Future Improvements:
- Consider factory pattern for platform instance creation
- Investigate keyed/named DI services to avoid registration conflicts
- Add unit tests for facade delegation logic
- Document migration guide for existing clients using platform implementations directly
Files Modified
New Files (2):
/Bluetooth.Maui/BluetoothScanner.cs(450 LOC)/Bluetooth.Maui/BluetoothBroadcaster.cs(500 LOC)
Modified Files (5):
/Bluetooth.Maui/ServiceCollectionExtensions.cs/Bluetooth.Maui.Platforms.Apple/Scanning/AppleBluetoothScanner.cs/Bluetooth.Maui.Platforms.Apple/Broadcasting/AppleBluetoothBroadcaster.cs/Bluetooth.Maui.Platforms.Win/Scanning/WindowsBluetoothScanner.cs/Bluetooth.Maui.Platforms.Droid/Scanning/AndroidBluetoothScanner.cs
Documentation Files:
/Docs/FACADE_PATTERN_SUMMARY.md(this file)
References
- Main Documentation:
/Docs/COPILOT_INSTRUCTIONS.md - Coding Standards: EventId ranges (1000-8999), Options pattern, CancellationToken standards
- Architecture Patterns: BaseBluetoothScanner, BaseBluetoothBroadcaster, Factory pattern
Status: ✅ Phase 1 Complete (Scanner & Broadcaster) Next: Phase 2 - Device/Service/Characteristic facades Estimated Completion: 40 additional hours