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 IBluetoothScanner interface (~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)
  • 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 IBluetoothBroadcaster interface:

    • 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
  • 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.ServiceListChangedEventArgs
    • Abstractions.Broadcasting.Options.PermissionOptions
    • Platforms.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:

  1. Reordered parameters to match BaseBluetoothScanner/BaseBluetoothBroadcaster
  2. Fixed circular dependencies (Apple): Wrappers (CbCentralManagerWrapper, CbPeripheralManagerWrapper) now created internally by platform classes instead of injected via DI
  3. Removed wrapper DI registrations from platform-specific ServiceCollectionExtensions
  4. Added missing parameters (Windows scanner - IBluetoothNameProvider)

Architecture Patterns

Composition Over Inheritance:

  • Facades implement IBluetoothScanner/IBluetoothBroadcaster directly
  • 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:

  1. Single Inheritance Point: Inherit BluetoothScanner/BluetoothBroadcaster once, works on all platforms
  2. No Platform Knowledge Required: Clients don't need #if directives for basic customization
  3. Virtual Extension Points: Override specific behaviors without reimplementing entire interface
  4. Type Safety: Full IntelliSense and compile-time checking across all platforms

For Maintainers:

  1. Centralized Logic: Common cross-platform behavior in one place
  2. Platform Flexibility: Platform-specific implementations remain independent
  3. Layer Separation: Facade layer clearly separated from platform layer
  4. DI Integration: Standard dependency injection patterns maintained

Testing Verification

✅ Compilation Checks:

  • BluetoothScanner.cs: ✅ No errors
  • BluetoothBroadcaster.cs: ✅ No errors
  • ServiceCollectionExtensions.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:

  1. BluetoothRemoteDevice - Device discovered during scanning
  2. BluetoothRemoteService - GATT service on remote device
  3. BluetoothRemoteCharacteristic - GATT characteristic on remote service
  4. BluetoothRemoteDescriptor - GATT descriptor on remote characteristic
  5. BluetoothConnectedDevice - Device connected to broadcaster
  6. BluetoothLocalService - GATT service hosted by broadcaster
  7. BluetoothLocalCharacteristic - GATT characteristic in local service
  8. 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:

  1. Dual DI Registration: Platform implementations still registered by platform-specific extensions, then overridden by facade registrations. Consider removing platform registrations or using keyed services.
  2. Direct Instantiation: Facades create platform instances directly instead of via DI, bypassing potential DI customization hooks.
  3. XML Documentation: Conditional compilation in XML docs required workaround (documented all params including platform-specific ones).

Future Improvements:

  1. Consider factory pattern for platform instance creation
  2. Investigate keyed/named DI services to avoid registration conflicts
  3. Add unit tests for facade delegation logic
  4. 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