don / cordova-plugin-ble-central

Bluetooth Low Energy (BLE) Central plugin for Apache Cordova (aka PhoneGap)
Apache License 2.0
946 stars 609 forks source link

BLE scan observable not notifying on first click on iOS platform (Ionic React Capacitor project) #828

Open imranaftab opened 3 years ago

imranaftab commented 3 years ago

Hi there,

I am working on latest Ionic v5 with React and capacitor. I have included BLE package and also added all required permissions. The problem I am facing right now is on iOS platform with BLE scan() method (the same code works perfectly fine on Android). On iOS, BLE.scan() method doesn't notify the subscribe callback when any peripheral is discovered. It only happens the first time I click the button. When I click the scan button again, it works as expected and retrieves the discovered peripheral. Can someone suggest something? Thanks in advance!

For reference, I have checked-in a sample project with this issue on public repo here: https://github.com/imranaftab/Ionic-react-capacitor-BLE-scan-issue-

Below is the sample code that causes problem on iOS platform:

import React, {useState} from 'react';
import { IonButton, IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
import ExploreContainer from '../components/ExploreContainer';
import './Tab1.css';
import { BLE } from '@ionic-native/ble';

const Tab1: React.FC = () => {
  let devices: string[] = []
  const [response, setResponse] = useState('')
  let scan = ()=>{
    BLE.scan([],4).subscribe((device) =>{
      console.log(device);
      if(device && device.name){
        devices = [...devices, device.name];
        setResponse(devices.reduce((a,b)=> a + ', ' + b ));
      }

    });
  };

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Tab 1</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Tab 1</IonTitle>
          </IonToolbar>
        </IonHeader>
        <IonButton onClick={()=> scan()}>Scan</IonButton>
        <p>{response}</p>
      </IonContent>
    </IonPage>
  );
};

export default Tab1;
tijmenvangulik commented 3 years ago

I have the same problem when I run startScan() on ios13. (in my case I do not use ionic or react, just plain java script) When I look at the log the first time I try to connect the request is blocked by an error message "can only accept this command while in the powered on state" . The second time that I try to connect it works fine.

2021-01-06 18:20:54.484020+0100 Ergometer space[20308:7717741] startScan 2021-01-06 18:20:54.484117+0100 Ergometer space[20308:7717741] [CoreBluetooth] API MISUSE: <CBCentralManager: 0x283f9df00> can only accept this command while in the powered on state 2021-01-06 18:20:54.514358+0100 Ergometer space[20308:7717741] Status of CoreBluetooth central manager changed 5 State powered up and ready (CBCentralManagerStatePoweredOn)

A search on the internet states that it ble central to wait for the powered on state:

https://stackoverflow.com/questions/23338767/ios-core-bluetooth-getting-api-misuse-warning

When I look at the log , the log of the powered on state is later than the scan log. So the scan is here not waiting for the powered on state. That is exactly what the stack overflow describes.

I am hover not an objective c expert so I do not yet know how to fix it.

For my own project I made a small work around in javascript. I first call the isEnabled before the startScan and when it returns an error I wait a second and then call the it is enabled I call startScan and when not I return an error. The advantage of the workaround is that I get a proper error when blue tooth is switched off.

But it would be much better if it is fixed in the ble-central plugin.

Path-A commented 3 years ago

Have the same issue with my Ionic-Vue / Capacitor project. The workaround @tijmenvangulik mentioned works, but a proper solution would be nice.

JEricaM commented 3 years ago

Same issue here. I will try @tijmenvangulik workaround!

delvinwidjaja commented 3 years ago

Does anyone know if this bug has been fixed or will be fixed?

Path-A commented 3 years ago

No, it is not fixed. You still have to use a workaround or a different BLE library.

delvinwidjaja commented 3 years ago

I know this is an issue page, but I really want to know if my workaround is either correct or could possibly produce another error.

Below is my method:

public scan() {
    console.log('Start scanning');
    this.devices = []; // clear list

    // Scan for 7 seconds
    this.ble
      .isEnabled()
      .then(() => {
        this.ble.scan([], 7).subscribe(
          (device) => this.onDeviceDiscovered(device),
          (error) => this.onScanError(error)
        );
      })
      .catch((err) => {
        // Try to connect again because bluetooth is disabled
        console.log(`Bluetooth is not enabled. Error: ${err}`);
        this.scan();
      });
  }

Basically, on the first click, the catch block will be called and the second attempt to scan will be executed. In this state, the bluetooth will be in the enabled state. Is this the right way to fix this issue?

peitschie commented 3 years ago

I've just encountered this issue recently. It's likely caused by the lazy-loading of the BLE bridge here: https://github.com/imranaftab/Ionic-react-capacitor-BLE-scan-issue-/blob/61d5021df934f1fe7150a257802edbdea89b4baa/ios/App/App/config.xml#L6

This is on my radar to fix in the coming weeks.

@delvinwidjaja your described workaround there should be good enough. Giving CBCentralManager a second or two to "warm up" is enough to avoid this issue in practice.

graphefruit commented 2 years ago

Great this one is getting fixed :) Thanks for this. Funny enough its not an issue on every iOS device I've encountered on iPhone 8 its working, on iPhone 11 its not working on my side

peitschie commented 2 years ago

Noting for myself, one attempt which might affect this was previously made at https://github.com/don/cordova-plugin-ble-central/pull/735

That approach is what I'd entertain here, will need to figure out the threading issue described there however.