PhilipsHue / flutter_reactive_ble

Flutter library that handles BLE operations for multiple devices.
https://developers.meethue.com/
Other
661 stars 321 forks source link

scanForDevices should not use 'assert(!central.isScanning)' #838

Open nissaba opened 6 months ago

nissaba commented 6 months ago

In the function

func scanForDevices(name: String, args: ScanForDevicesRequest, completion: @escaping PlatformMethodCompletionHandler) {
        guard let central = central
        else {
            completion(.failure(PluginError.notInitialized.asFlutterError))
            return
        }

        assert(!central.isScanning)

        scan = StreamingTask(parameters: .init(services: args.serviceUuids.map({ uuid in CBUUID(data: uuid.data) })))

        completion(.success(nil))
    }

you should not use the assert(!central.isScanning) as this will crash an app that is trying connect to a device and wants to move to scaning to all device, the libray does not offer a way (not that I have seen) to stop/interupt this process.

This is the case for my app. When starting the app I will call connectToAdvertisingDevice to find the last connected device or to reconnect to the curent used device. If the user at some point wants to scan for an other device, the code needs to have a way to kill the process of this scanForDevices, as there si no way to interupt it asking to discover new devices will lead to a crash.

What should the code do? it should stop/kill the current decovery process and initiate the new request call.

func scanForDevices(name: String, args: ScanForDevicesRequest, completion: @escaping PlatformMethodCompletionHandler) {
        guard let central = central
        else {
            completion(.failure(PluginError.notInitialized.asFlutterError))
            return
        }

        if central.isScanning {
            central.stopScan()
            //other cleanup code
        }

        scan = StreamingTask(parameters: .init(services: args.serviceUuids.map({ uuid in CBUUID(data: uuid.data) })))

        completion(.success(nil))
    }
nissaba commented 6 months ago

ping!

Taym95 commented 5 months ago

Hi thanks for report, PR are welcome

Albert-Jan commented 1 month ago

Facing the same issue, any tips/recommendations would be appreciated

i13Proietti commented 3 weeks ago

I'm in trouble too with this problem. Waiting for the solution

YueLiXing commented 1 week ago

I have found the right way

class ShareReactiveBle {
  static Uuid gattUUID = Uuid.parse('****');
  static Uuid meshUUID = Uuid.parse('****');
  static final ShareReactiveBle _instance = ShareReactiveBle._internal();

  factory ShareReactiveBle() => _instance;

  late final FlutterReactiveBle _ble;
  StreamSubscription<DiscoveredDevice>? _subscription;
  final StreamController<DiscoveredDevice> _stateStreamController = StreamController();

  late Stream<DiscoveredDevice> stream;

  FlutterReactiveBle get ble => _ble;

  ShareReactiveBle._internal() {
    _ble = FlutterReactiveBle();
    _ble.statusStream.listen((event) {
      Log.d('statusStream $event');
    });
    stream = _stateStreamController.stream.asBroadcastStream();
  }

  void scan() {
    _subscription?.cancel();
    final filterService = Platform.isIOS ? [gattUUID, meshUUID] : <Uuid>[];

    _subscription = _ble.scanForDevices(withServices: filterService, scanMode: ScanMode.lowLatency).listen((event) {
      _stateStreamController.add(event);
    }, onError: (exception) {
      _stateStreamController.addError(exception);
      stopScan();
    });
  }

  void stopScan() {
    _subscription?.cancel();
  }
}