Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

1amageek/Bleu

Open more actions menu

Repository files navigation

Bleu Logo

Bleu 2

Modern Bluetooth Low Energy Framework with Swift Distributed Actors

Swift 6.0 Platforms Swift Package Manager Test License Sponsor


Overview

Bleu 2 is a revolutionary Bluetooth Low Energy framework that leverages Swift's Distributed Actor System and @Resolvable protocols to create seamless, type-safe communication between BLE devices.

Define a protocol, implement it, and call methods over BLEβ€”it's that simple.

No complex BLE APIs. No manual serialization. No boilerplate code. Just define your distributed actor protocol with @Resolvable, and Bleu handles everything else automatically.

✨ Key Features

🎯 Protocol-Oriented BLE with @Resolvable

  • Define a protocol with distributed methods
  • Implement on peripheral as a distributed actor
  • Resolve on central using auto-generated stubs
  • Call methods over BLE as if they were local
  • Zero boilerplate, maximum simplicity

🎭 Distributed Actor Architecture

  • Transparent RPC over BLE using Swift's native distributed actors
  • Type-safe remote method invocation
  • Automatic serialization and error handling
  • Actor isolation for thread safety

πŸš€ High Performance

  • Binary packet fragmentation with 24-byte headers
  • Efficient data transport with checksums
  • Adaptive MTU negotiation
  • Automatic packet reassembly

πŸ“± Modern Swift Integration

  • Full async/await support
  • AsyncStream for real-time data
  • Swift 6 concurrency features
  • Sendable protocol compliance

πŸ”§ Developer Friendly

  • Simple, intuitive API
  • Comprehensive logging system
  • Automatic resource management
  • Clean error handling

πŸš€ Quick Start

Installation

Add Bleu to your project using Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/1amageek/Bleu.git", from: "2.2.0")
]

The Simplest Way: Using @Resolvable

import Bleu
import Distributed

// 1. Define your BLE device API as a protocol
@Resolvable
protocol TemperatureSensor: PeripheralActor {
    distributed func getTemperature() async throws -> Double
    distributed func setUpdateInterval(_ seconds: Int) async throws
}

// 2. Peripheral: Implement the protocol
distributed actor MyThermometer: TemperatureSensor {
    typealias ActorSystem = BLEActorSystem

    distributed func getTemperature() async throws -> Double {
        return 25.5  // Read from actual sensor
    }

    distributed func setUpdateInterval(_ seconds: Int) async throws {
        // Configure update rate
    }
}

// 3. Central: Resolve and call methods over BLE
let actorSystem = BLEActorSystem(
    peripheralManager: CoreBluetoothPeripheralManager(),
    centralManager: CoreBluetoothCentralManager()
)

// Discover the sensor
let sensors = try await actorSystem.discover(MyThermometer.self, timeout: 10.0)

// Resolve using the protocol (works even if you only know the ID!)
let sensor = try $TemperatureSensor.resolve(id: sensors[0].id, using: actorSystem)

// Call methods as if the sensor were local
let temp = try await sensor.getTemperature()  // πŸŽ‰ That's it!

Traditional Approach: Concrete Actor Types

You can also define distributed actors directly without protocols:

import Bleu
import Distributed

// Define a distributed actor that runs on a BLE peripheral
distributed actor TemperatureSensor: PeripheralActor {
    typealias ActorSystem = BLEActorSystem

    distributed func getTemperature() async throws -> Double {
        // Read from actual sensor hardware
        return 25.5
    }

    distributed func setUpdateInterval(_ seconds: Int) async throws {
        // Configure sensor update rate
    }
}

Peripheral Side

// Create BLE actor system with CoreBluetooth managers
let peripheralManager = CoreBluetoothPeripheralManager()
let centralManager = CoreBluetoothCentralManager()
let actorSystem = BLEActorSystem(
    peripheralManager: peripheralManager,
    centralManager: centralManager
)

// Create and advertise the sensor
let sensor = TemperatureSensor(actorSystem: actorSystem)

// Start advertising the sensor service
try await actorSystem.startAdvertising(sensor)

Central Side

// Create BLE actor system with CoreBluetooth managers
let peripheralManager = CoreBluetoothPeripheralManager()
let centralManager = CoreBluetoothCentralManager()
let actorSystem = BLEActorSystem(
    peripheralManager: peripheralManager,
    centralManager: centralManager
)

// Discover and connect to sensors
let sensors = try await actorSystem.discover(TemperatureSensor.self, timeout: 10.0)

if let remoteSensor = sensors.first {
    // Call methods on the remote sensor as if it were local!
    let temperature = try await remoteSensor.getTemperature()
    print("Current temperature: \(temperature)Β°C")

    // Configure the remote sensor
    try await remoteSensor.setUpdateInterval(5)
}

🎯 Advanced Features

Protocol-Based Actor Resolution with @Resolvable

Bleu 2 leverages Swift's @Resolvable macro (SE-0428) to enable protocol-oriented distributed actor design. You can define your own protocols with distributed methods and use the compiler-generated stubs to resolve remote actors without knowing their concrete implementations.

Why Use @Resolvable?

  • Protocol-First API Design: Define your BLE device APIs as protocols
  • Implementation Flexibility: Peripheral implementations remain private
  • Type-Safe Resolution: Resolve actors by ID using protocol types
  • Module Separation: Share protocol definitions across app modules
  • Easy Testing: Mock protocol implementations for unit tests

Define Your Own Protocol

Important: Add @Resolvable to your custom protocols, not to the base PeripheralActor protocol. The PeripheralActor protocol is a marker protocol without distributed methods.

// Step 1: Define a custom protocol with @Resolvable and distributed methods
@Resolvable
protocol TemperatureSensor: PeripheralActor {
    distributed func getTemperature() async throws -> Double
    distributed func setTemperatureUnit(_ unit: String) async throws
}

// Step 2: Peripheral side - Implement the protocol
distributed actor IndoorSensor: TemperatureSensor {
    typealias ActorSystem = BLEActorSystem

    private var unit = "celsius"

    distributed func getTemperature() async throws -> Double {
        return unit == "celsius" ? 25.5 : 77.9
    }

    distributed func setTemperatureUnit(_ unit: String) async throws {
        self.unit = unit
    }
}

// Step 3: Central side - Work with the protocol, not the concrete type
let actorSystem = BLEActorSystem(
    peripheralManager: CoreBluetoothPeripheralManager(),
    centralManager: CoreBluetoothCentralManager()
)

// Option 1: Discover sensors using concrete type
let sensors = try await actorSystem.discover(IndoorSensor.self, timeout: 10.0)
if let sensor = sensors.first {
    let temp = try await sensor.getTemperature()
}

// Option 2: Resolve by ID using @Resolvable-generated stub
// The @Resolvable macro generates a $TemperatureSensor type automatically
let knownSensorID = UUID(/* saved sensor ID */)
let sensor = try $TemperatureSensor.resolve(id: knownSensorID, using: actorSystem)

// Call methods defined in the protocol
try await sensor.setTemperatureUnit("fahrenheit")
let temp = try await sensor.getTemperature()

// Pass around as protocol type - no concrete type needed!
func monitorTemperature(_ sensor: any TemperatureSensor) async throws {
    let temp = try await sensor.getTemperature()
    print("Current temperature: \(temp)")
}
try await monitorTemperature(sensor)

Use Cases

// Example: Multiple sensor types with same protocol
@Resolvable
protocol EnvironmentSensor: PeripheralActor {
    distributed func readValue() async throws -> Double
    distributed func calibrate() async throws
}

distributed actor TemperatureSensor: EnvironmentSensor { /* ... */ }
distributed actor HumiditySensor: EnvironmentSensor { /* ... */ }
distributed actor PressureSensor: EnvironmentSensor { /* ... */ }

// Central can work with all sensors uniformly
func readAllSensors(_ sensors: [any EnvironmentSensor]) async throws -> [Double] {
    try await withThrowingTaskGroup(of: Double.self) { group in
        for sensor in sensors {
            group.addTask { try await sensor.readValue() }
        }

        var values: [Double] = []
        for try await value in group {
            values.append(value)
        }
        return values
    }
}

Key Benefits

  • Protocol-First Design: Define your device APIs as protocols with distributed methods
  • Automatic Stub Generation: Swift compiler generates $ProtocolName types for resolution
  • Location Transparency: Work with protocol types without knowing concrete implementations
  • Module Separation: Share protocol definitions across modules, keep implementations private
  • Type Safety: Full compiler verification of distributed method calls
  • Flexible Resolution: Resolve actors by ID without discovery process

Custom Service Metadata

distributed actor SmartLight: PeripheralActor {
    typealias ActorSystem = BLEActorSystem
    
    // Custom service configuration
    static var serviceMetadata: ServiceMetadata {
        ServiceMetadata(
            uuid: UUID(uuidString: "12345678-1234-5678-9ABC-123456789ABC")!,
            characteristics: [
                CharacteristicMetadata(
                    uuid: UUID(uuidString: "87654321-4321-8765-CBA9-987654321CBA")!,
                    properties: [.read, .write, .notify],
                    permissions: [.readable, .writeable]
                )
            ]
        )
    }
    
    distributed func setBrightness(_ level: Int) async throws {
        // Control light brightness
    }
    
    distributed func setColor(_ rgb: (r: Int, g: Int, b: Int)) async throws {
        // Set RGB color
    }
}

Notifications and Subscriptions

distributed actor HeartRateMonitor: PeripheralActor {
    typealias ActorSystem = BLEActorSystem
    
    // Stream heart rate data
    distributed func streamHeartRate() -> AsyncStream<Int> {
        AsyncStream { continuation in
            // Setup sensor monitoring
            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
                let heartRate = Int.random(in: 60...100)
                continuation.yield(heartRate)
            }
        }
    }
}

// Client side - subscribe to updates
let monitor = try await actorSystem.connect(to: deviceID, as: HeartRateMonitor.self)
for await heartRate in await monitor.streamHeartRate() {
    print("Heart rate: \(heartRate) BPM")
}

Error Handling

do {
    let devices = try await actorSystem.discover(TemperatureSensor.self)
    // ... use devices
} catch BleuError.bluetoothPoweredOff {
    print("Please enable Bluetooth")
} catch BleuError.connectionTimeout {
    print("Connection timed out")
} catch {
    print("Unexpected error: \(error)")
}

πŸ“‹ Architecture

Core Components

  • BLEActorSystem: The distributed actor system managing BLE communication
  • PeripheralActor: Protocol for actors that can be advertised as BLE peripherals
  • BLETransport: Handles packet fragmentation and reassembly
  • ServiceMapper: Maps actor types to BLE service metadata
  • Manager Protocols: Protocol-oriented design for testability without hardware

Communication Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Central   β”‚                    β”‚ Peripheral  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚                                   β”‚
       β”‚  discover(TemperatureSensor)      β”‚
       │────────────────────────────────>  β”‚
       β”‚                                   β”‚
       β”‚  <────────────────────────────    β”‚
       β”‚     [TemperatureSensor actors]    β”‚
       β”‚                                   β”‚
       β”‚  remoteSensor.getTemperature()    β”‚
       │────────────────────────────────>  β”‚
       β”‚                                   β”‚
       β”‚  <────────────────────────────    β”‚
       β”‚            25.5Β°C                 β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Binary Packet Format

Bleu uses an efficient binary packet format for BLE communication:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  UUID (16B)  β”‚  Seq (2B)   β”‚  Total (2B)  β”‚ CRC (4B) β”‚ Payload
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        24-byte header

πŸ§ͺ Testing

Bleu 2 features a Protocol-Oriented Testing Architecture that enables comprehensive testing without requiring real Bluetooth hardware or TCC permissions. This architecture provides in-memory BLE simulation, allowing unit and integration tests to run in CI/CD environments.

Key Testing Benefits

  • βœ… No Hardware Required: Mock implementations simulate complete BLE behavior
  • βœ… No TCC Permissions: Unit and integration tests run without Bluetooth access
  • βœ… Fast Execution: Tests complete in seconds, not minutes
  • βœ… CI/CD Friendly: All tests (except hardware validation) run in automated environments
  • βœ… Type-Safe: Full type safety across mock and production implementations

Test Directory Structure

Tests/BleuTests/
β”œβ”€β”€ Unit/                          # Pure unit tests (no BLE)
β”‚   β”œβ”€β”€ UnitTests.swift            # Core functionality tests
β”‚   β”œβ”€β”€ RPCTests.swift             # RPC mechanism tests
β”‚   β”œβ”€β”€ EventBridgeTests.swift    # Event routing tests
β”‚   └── TransportLayerTests.swift # Message transport tests
β”‚
β”œβ”€β”€ Integration/                   # Mock-based integration tests
β”‚   β”œβ”€β”€ MockActorSystemTests.swift # Mock manager tests
β”‚   β”œβ”€β”€ FullWorkflowTests.swift    # Complete workflows
β”‚   β”œβ”€β”€ ErrorHandlingTests.swift   # Error scenarios
β”‚   └── MinimalEmulatorTest.swift  # CoreBluetoothEmulator tests
β”‚
β”œβ”€β”€ Hardware/                      # Real BLE hardware tests
β”‚   └── RealBLETests.swift         # Requires real hardware
β”‚
└── Mocks/                         # Test utilities
    β”œβ”€β”€ TestHelpers.swift          # Common test helpers
    β”œβ”€β”€ MockActorExamples.swift    # Example distributed actors
    └── Emulator/                  # CoreBluetoothEmulator adapters
        β”œβ”€β”€ EmulatedBLECentralManager.swift    # Emulator central adapter
        └── EmulatedBLEPeripheralManager.swift # Emulator peripheral adapter

Running Tests

# Run all tests (unit + integration, skips hardware)
swift test

# Run specific test suite
swift test --filter "Mock Actor System Tests"

# Run with verbose output
swift test --verbose

# Parallel execution
swift test --parallel

Quick Start: Writing Tests

Unit Tests (No BLE Dependency)

import Testing
@testable import Bleu

@Suite("Transport Layer")
struct TransportTests {
    @Test("Packet fragmentation")
    func testFragmentation() async {
        let transport = BLETransport.shared
        let data = Data(repeating: 0xFF, count: 1000)

        let packets = await transport.fragment(data)
        #expect(packets.count > 1)
    }
}

Integration Tests with Mock BLE

import Testing
import Distributed
@testable import Bleu

@Suite("BLE System Integration")
struct MockBLESystemTests {
    @Test("Complete discovery to RPC flow")
    func testCompleteFlow() async throws {
        // Create bridge for cross-system communication
        let bridge = MockBLEBridge()

        // Create peripheral system with mock managers
        var peripheralConfig = TestHelpers.fastPeripheralConfig()
        peripheralConfig.bridge = bridge

        let mockPeripheral1 = MockPeripheralManager(configuration: peripheralConfig)
        let mockCentral1 = MockCentralManager()
        let peripheralSystem = BLEActorSystem(
            peripheralManager: mockPeripheral1,
            centralManager: mockCentral1
        )

        // Create central system with mock managers
        var centralConfig = TestHelpers.fastCentralConfig()
        centralConfig.bridge = bridge

        let mockPeripheral2 = MockPeripheralManager()
        let mockCentral2 = MockCentralManager(configuration: centralConfig)
        let centralSystem = BLEActorSystem(
            peripheralManager: mockPeripheral2,
            centralManager: mockCentral2
        )

        // Wait for systems to be ready
        try await TestHelpers.waitForReady(peripheralSystem)
        try await TestHelpers.waitForReady(centralSystem)

        // Define test actor
        distributed actor TestSensor: PeripheralActor {
            typealias ActorSystem = BLEActorSystem

            distributed func getValue() async -> Int {
                return 42
            }
        }

        // Setup peripheral
        let sensor = TestSensor(actorSystem: peripheralSystem)
        await mockPeripheral1.setPeripheralID(sensor.id)
        try await peripheralSystem.startAdvertising(sensor)

        // Register peripheral for discovery
        let serviceUUID = UUID.serviceUUID(for: TestSensor.self)
        let serviceMetadata = ServiceMapper.createServiceMetadata(from: TestSensor.self)

        let discovered = TestHelpers.createDiscoveredPeripheral(
            id: sensor.id,
            name: "TestSensor",
            serviceUUIDs: [serviceUUID]
        )

        await mockCentral2.registerPeripheral(discovered, services: [serviceMetadata])

        // Test discovery and RPC
        let sensors = try await centralSystem.discover(TestSensor.self, timeout: 1.0)
        #expect(sensors.count == 1)

        let value = try await sensors[0].getValue()
        #expect(value == 42)
    }
}

Using Test Helpers

Bleu provides comprehensive test utilities:

// Fast mock configurations (10ms delays)
let peripheralConfig = TestHelpers.fastPeripheralConfig()
let centralConfig = TestHelpers.fastCentralConfig()

let mockPeripheral = MockPeripheralManager(configuration: peripheralConfig)
let mockCentral = MockCentralManager(configuration: centralConfig)
let system = BLEActorSystem(
    peripheralManager: mockPeripheral,
    centralManager: mockCentral
)

// Generate test data
let randomData = TestHelpers.randomData(size: 100)
let deterministicData = TestHelpers.deterministicData(size: 100, pattern: 0xAB)

// Create test peripherals
let peripheral = TestHelpers.createDiscoveredPeripheral(
    id: UUID(),
    name: "TestDevice",
    rssi: -50,
    serviceUUIDs: [serviceUUID]
)

// Pre-built test actors
let sensor = SensorActor(actorSystem: system)
let temp = try await sensor.readTemperature() // Returns 22.5

Mock Actor Examples

Pre-defined distributed actors for common testing scenarios:

// Simple value actor
let actor = SimpleValueActor(actorSystem: system)
let value = try await actor.getValue() // Returns 42

// Stateful counter actor
let counter = CounterActor(actorSystem: system)
let count = try await counter.increment() // Returns 1

// Error-throwing actor
let errorActor = ErrorThrowingActor(actorSystem: system)
try await errorActor.alwaysThrows() // Throws TestError

CoreBluetoothEmulator Integration

Bleu integrates with CoreBluetoothEmulator for full-fidelity BLE testing without hardware. This provides realistic BLE behavior including MTU negotiation, packet fragmentation, and connection management.

Features

  • βœ… Full BLE Emulation: Complete EmulatorBus routing between central and peripheral
  • βœ… No Hardware Required: Tests run on CI/CD without real Bluetooth devices
  • βœ… Instant Execution: .instant configuration for fast unit tests
  • βœ… Realistic Timing: .default, .slow, .unreliable configurations for integration tests
  • βœ… Event-Driven Architecture: Proper async/await flow with delegate callbacks

Quick Start

import Testing
import CoreBluetoothEmulator
@testable import Bleu

@Test("Full BLE connection flow")
func testEmulatorConnection() async throws {
    // Configure EmulatorBus for fast testing
    await EmulatorBus.shared.reset()
    await EmulatorBus.shared.configure(.instant)

    // Create peripheral with emulator
    var peripheralConfig = EmulatedBLEPeripheralManager.Configuration()
    peripheralConfig.emulatorPreset = .instant
    let peripheral = EmulatedBLEPeripheralManager(configuration: peripheralConfig)
    await peripheral.initialize()
    _ = await peripheral.waitForPoweredOn()

    // Add service and start advertising
    let service = ServiceMetadata(
        uuid: serviceUUID,
        isPrimary: true,
        characteristics: [
            CharacteristicMetadata(
                uuid: charUUID,
                properties: [.read, .notify],
                permissions: [.readable]
            )
        ]
    )
    try await peripheral.add(service)
    try await peripheral.startAdvertising(
        AdvertisementData(localName: "TestDevice", serviceUUIDs: [serviceUUID])
    )

    // Create central and scan
    var centralConfig = EmulatedBLECentralManager.Configuration()
    centralConfig.emulatorPreset = .instant
    let central = EmulatedBLECentralManager(configuration: centralConfig)
    await central.initialize()
    _ = await central.waitForPoweredOn()

    // Discover and connect
    var discoveredID: UUID?
    for await discovered in central.scanForPeripherals(
        withServices: [serviceUUID],
        timeout: 1.0
    ) {
        discoveredID = discovered.id
        break
    }

    try await central.connect(to: discoveredID!, timeout: 2.0)
    // Test passes in 0.001 seconds!
}

Emulator Configurations

// Instant - for fast unit tests (no delays)
peripheralConfig.emulatorPreset = .instant

// Default - realistic timing for development
peripheralConfig.emulatorPreset = .default

// Slow - poor connection simulation
peripheralConfig.emulatorPreset = .slow

// Unreliable - error and failure simulation
peripheralConfig.emulatorPreset = .unreliable

// Custom configuration
var config = EmulatorConfiguration.default
config.stateUpdateDelay = 0.01
config.advertisingDelay = 0.05
peripheralConfig.customEmulatorConfig = config

Architecture

The emulator adapters (EmulatedBLECentralManager and EmulatedBLEPeripheralManager) implement Bleu's manager protocols and bridge CoreBluetoothEmulator's delegate-based API to Bleu's async/await actor-based API:

  • Delegate β†’ AsyncStream: Converts CBCentralManagerDelegate/CBPeripheralManagerDelegate callbacks to AsyncStream events
  • Event-Driven: Uses continuation-based waiting instead of polling or delays
  • EmulatorBus Routing: Proper event routing between managers via shared bus
  • Full Protocol Conformance: Implements BLECentralManagerProtocol and BLEPeripheralManagerProtocol

See Tests/BleuTests/Integration/MinimalEmulatorTest.swift for complete examples.

Mock System Configuration

Mocks support extensive configuration for testing various scenarios:

// Configure mock delays
var peripheralConfig = MockPeripheralManager.Configuration()
peripheralConfig.advertisingDelay = 0.01 // Fast for testing
peripheralConfig.writeResponseDelay = 0.01

var centralConfig = MockCentralManager.Configuration()
centralConfig.scanDelay = 0.01
centralConfig.connectionDelay = 0.01

// Configure failure scenarios
peripheralConfig.shouldFailAdvertising = true
centralConfig.shouldFailConnection = true

let mockPeripheral = MockPeripheralManager(configuration: peripheralConfig)
let mockCentral = MockCentralManager(configuration: centralConfig)
let system = BLEActorSystem(
    peripheralManager: mockPeripheral,
    centralManager: mockCentral
)

Hardware Tests

Hardware tests require real BLE hardware and TCC permissions. They are disabled by default:

@Suite("Real BLE Hardware Tests", .disabled("Requires real BLE hardware"))
struct RealBLETests {
    @Test("Real device communication")
    func testRealDevice() async throws {
        // Create system with real CoreBluetooth managers (requires TCC)
        let peripheralManager = CoreBluetoothPeripheralManager()
        let centralManager = CoreBluetoothCentralManager()
        let system = BLEActorSystem(
            peripheralManager: peripheralManager,
            centralManager: centralManager
        )
        // ... test with real hardware
    }
}

To run hardware tests, remove the .disabled attribute and ensure your app has proper Info.plist with Bluetooth permissions.

CI/CD Integration

GitHub Actions example:

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: macos-26
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: swift test

Hardware tests are automatically skipped in CI/CD due to .disabled attribute.

Comprehensive Testing Guide

For complete documentation including:

  • Detailed architecture explanation
  • Mock system usage patterns
  • Simulating BLE events
  • Error handling tests
  • Best practices
  • Troubleshooting guide

See the Complete Testing Guide.

πŸ“± Platform Requirements

  • iOS 18.0+ / macOS 15.0+ / watchOS 11.0+ / tvOS 18.0+
  • Swift 6.0+
  • Xcode 16.0+

πŸ“š Documentation

Comprehensive documentation is available in the docs/ directory:

For contributors and maintainers:

πŸ“„ License

Bleu is available under the MIT license. See the LICENSE file for more info.


Sponsor

Made with ❀️ by @1amageek

Documentation β€’ Issues β€’ Discussions

About

BLE (Bluetooth LE) for U🎁 Bleu is the best in the Bluetooth library.

Topics

Resources

Code of conduct

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.