PhilipsHue / flutter_reactive_ble

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

Disable bluetooth while connecting leaves the lib in a bad state on iOS #579

Open atbamparo opened 2 years ago

atbamparo commented 2 years ago

Describe the bug Hi, during some tests I noticed if we start a connection and before the connection is succeeded, if I disable the iOS bluetooth the lib will be on a state that its not accepting more connection requests. It will starts falling under this assert https://github.com/PhilipsHue/flutter_reactive_ble/blob/04384dd096d9197e26120ca3c816b82cf85949f5/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Tasks/Connect/ConnectTaskController.swift#L16

To Reproduce Steps to reproduce the behavior (iOS):

  1. Turn off the peripheral device.
  2. Try to connect with the bonded (disabled) peripheral device.
  3. Turn off the bluetooth
  4. Wait ~5 seconds
  5. Turn on the peripheral device (optional), try to connect again

Expected behavior The app should try to connect

Smartphone

atbamparo commented 2 years ago

Sample app that reproduces the issue

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';

const deviceId = '<bonded deviceID>';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[ConnectButton()],
        ),
      ),
    );
  }
}

class ConnectButton extends StatefulWidget {
  const ConnectButton({Key? key}) : super(key: key);

  @override
  ConnectButtonState createState() => ConnectButtonState();
}

class ConnectButtonState extends State<ConnectButton> {
  FlutterReactiveBle reactiveBle = FlutterReactiveBle();
  StreamSubscription? connection = null;

  Future<void> connect() async {
    connection = reactiveBle.connectToDevice(id: deviceId).listen((event) {
      print(event);
    });
    setState(() {});
  }

  Future<void> disconnect() async {
    await connection?.cancel();
    setState(() {
      connection = null;
    });
  }

  @override
  Widget build(BuildContext context) => connection == null
      ? TextButton(onPressed: connect, child: const Text('Connect'))
      : TextButton(onPressed: disconnect, child: const Text('Disconnect'));
}

Will need to add a bonded device id in line 6

atbamparo commented 2 years ago

Ive also downloaded the code, I did a change that possible fixes the issue:

changed https://github.com/PhilipsHue/flutter_reactive_ble/blob/04384dd096d9197e26120ca3c816b82cf85949f5/packages/reactive_ble_mobile/ios/Classes/ReactiveBle/Central.swift#L162

to

connectRegistry.updateTask(
            key: peripheralID,
            action: { $0.cancel(centralManager: centralManager, peripheral: peripheral, error: nil) }
        )

(same approach to disconnectAll method)

Not sure if its fix the issue in a graceful way :) need some help for this

Maybe related to #549

towynlin commented 2 years ago

I'm seeing crashes on that assertion too. I don't yet know what's happening in detail, but I'll let you know if I learn anything useful.

Albert-Jan commented 1 year ago

Any updates on this? I'm still receiving the same type of crash