CoreBluetooth shows devices/peripherals with Bluetooth turned off - ios

var peripherals = [CBPeripheral]()
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let _ = peripheral.name {
if !peripherals.contains(peripheral) {
peripherals.append(peripheral)
table.reloadData()
}
}
}
Using the delegate method above, called after centralManager.scanForPeripherals(withServices: nil), I am showing available peripherals(only those with name).
Since some of the available devices are also mine, I am sure that bluetooth is turned off on them.
So why does this method returns them as available? Are they cached somewhere or?

Related

Corebluetooth to fetch device names using Swift

I'm using CoreBluetooth framework in my iOS application to scan and connect to other bluetooth devices. I'm able to scan successfully for devices using below code:
var centralManager: CBCentralManager?
var peripherals = [CBPeripheral]()
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
In the CBCentralManagerDelegate, I implemented below code:
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if (central.state == .poweredOn){
self.centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if !peripherals.contains(peripheral) {
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
print("\(String(describing: localName))" )
peripherals.append(peripheral)
tableView.reloadData()
}
}
}
From above code localName is always nil. Am I missing any code?
To add more info,
When I am scanning for bluetooth devices in my Mac, I'm able to see the names of the devices. But when I am scanning for bluetooth devices on my iPhone, none of them are listed.
I cross checked and made sure that I enabled blue tooth on 4 more devices with me (iPad, 2 Android Phones and 1 Android TV)
Swift with core bluetooth library:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
let peripheralLocalName_advertisement = ((advertisementData as NSDictionary).value(forKey: "kCBAdvDataLocalName")) as? String
if (((advertisementData as NSDictionary).value(forKey: "kCBAdvDataLocalName")) != nil)
print(peripheralLocalName_advertisement)//peripheral name from advertismentData
print(peripheral.name)//peripheral name from peripheralData
peripherals.append(peripheral)
arrayPeripheral.append(advertisementData)
}
}
I changed in your code only source of name - instead of:
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
I use:
let localName = peripheral.name
and now I see name of BLE devices around me.
I changed in your code only source of name - instead of:
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
I use:
let localName = peripheral.name
and now I see name of BLE devices around me.
The didDiscoverPeripheral callback unfortunately can return nil for CBPeripheral names and also the advertisementData's local name for the very first callback your app gets after you start a scan.
If you know that the peripheral that you're working with will contain a local name in the Bluetooth advertisement packet, you can choose to ignore the first callback that contains a nil name.
// In didDiscoverPeripheral
guard let peripheralName = peripheral.name else { return }
print("Did discover peripheral: \(peripheralName), RSSI: \(RSSI)")

CBCentralManager not getting connected

I'm trying to connect near by bluetooth devices. I'm able to discover but i'm unable to connect them.
this delegate is working but after discovering i'm trying connect with one of the pheripheral
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let device = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? String {
print(device)
if device.contains("BODY") {
self.connectPeripheral = peripheral
self.connectPeripheral.delegate = self
self.manager.connect(connectPeripheral, options: nil)
self.manager.stopScan()
}
}
}
These two delegates methods are not invoking. one of these should be called.
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
//NSLog("Error %#", error.debugDescription)
print(error.debugDescription)
}
You need to maintain a strong reference to the CBPeripheral instance, because once you tell the Central to stop scanning, it will deallocate all its discovered peripherals.

how to connect with bluetooth low energy in ios swift?

I want to connect with ble peripheral.
But my code doesn't call didConect function
this is my code :
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let device = (advertisementData as NSDictionary)
.object(forKey: CBAdvertisementDataLocalNameKey)
as? NSString
if device?.contains(BEAN_NAME) == true {
print("Did discover peripheral", peripheral)
self.bluetoothManager.stopScan()
self._peripheral = peripheral
self._peripheral.delegate = self
central.connect(peripheral, options: nil)
}
}
func centralManager( central: CBCentralManager, didConnect peripheral: CBPeripheral) { //cant call this
print("connected to \(BEAN_NAME)")
peripheral.discoverServices(nil)
}
Logs :
BLE service is powered on
Did discover peripheral <CBPeripheral: 0x1740eef00, identifier = 4872623B-F872-443A-8A96-F4E1F84D6841, name = GoDoor in :), state = disconnected>
I created a demo project, which scans for Bluetooth LE devices and displays them in a list:
Check it out on github: quickies/BluetoothScanner.ios
Screenshot
I've solved this.
just need to change :
func centralManager(central: CBCentralManager, didConnect peripheral: CBPeripheral)
into this :
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
reference : Swift 3: Can't connect to peripheral via BLE
#IBAction func btnConnect(_ sender: UIButton){
self.appDelegate.bleManager.stopScan()
self.appDelegate.selectPeripheral = self.arrayPeripheral[sender.tag]
self.appDelegate.bleManager.connect(self.selectPeripheral, options: nil)
}

CoreBluetooth is disconnecting from unused peripherals due to an API Misuse

I am trying to connect to a MacBook Pro from an iPad with CoreBluetooth.
Here is my delegation for CBCentralManagerDelegate:
extension MasterViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Scanning for peripherals")
central.scanForPeripherals(withServices: nil, options: nil)
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.stopScan), userInfo: nil, repeats: true)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Did discover peripheral", peripheral)
central.connect(peripheral, options: nil)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Did connect to ", peripheral)
peripheral.delegate = self
self.remotePeripheral.append(peripheral)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {}
}
But when I scan I get this error in the log:
<Error>: [CoreBluetooth] API MISUSE: Cancelling connection for unused peripheral
Why does this happen?
Not sure why this worked, but I found that it works if I assign the peripherals delegate to self, and add the peripheral to an array before I connect to it.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Did discover peripheral", peripheral)
peripheral.delegate = self
self.remotePeripheral.append(peripheral)
central.connect(peripheral, options: nil)
}
I was seeing this error and for those who also are facing the same issue, my suggestion is that I wasn't storing the CBPeripheral device in my helper class. That seems unnecessary to do but for some reason it needs to be bounded internally I believe.
Well, Here's what I did:-
class BLEHelper: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate{
#Published var pairedDevice:CBPeripheral?=nil
...
and then in your didDiscover function:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
pairedDevice=peripheral
peripheral.delegate=self
myCentral.connect(peripheral, options:nil)
myCentral.stopScan()
}
This line here does the trick:- pairedDevice=peripheral

Do I have to wait for the didReadRSSI() function in Swift?

I would like to discover BLE devices in my area and store their current RSSI value. The discovering works but I'm note sure, if my func didDiscoverPeripheral is really save... I think that I should wait for the didReadRSSI func before I leave the didDiscoverPeripheral. But how can I realize that in an easy way and is my opinion right?
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
CBperipheral = [peripheral]
peripheral.readRSSI()
}
func peripheral(peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: NSError?)
{
//Store the peripheral specific RSSI, Name and identifier
}
I would suggest something like this -
var peripherals = Set<CBPeripheral>
var peripheralRSSIs = Dictionary<CBPeripheral,NSNumber>()
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
self.peripherals.insert(peripheral)
self.peripheralRSSIs[peripheral]=RSSI
central.connectPeripheral(peripheral,options:nil) // Connect if you want to be able to retrieve additional RSSI values
}
func centralManager(_ central: CBCentralManager,
didConnectPeripheral peripheral: CBPeripheral)
{
peripheral.delegate=self
peripheral.readRSSI()
}
func peripheral(peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: NSError?)
{
if (error != nil) {
print(error)
} else {
self.peripheralRSSIs[peripheral]=RSSI
}
}
I solved the problem like this:
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
CBperipheral = [peripheral]
myStrings.append(StringPair("\(peripheral.name!)", "\(peripheral.name!)", "\(RSSI)"))
//Coose the right peripheral and connect!
}
I don't need very up-to-date values for the RSSI, only some guiding values. So I hope this code is conform with IOS 8 and later.
But it would be nice, if you could tell me the handling with the callback to my didReadRSSI for other problems like this.
btw. I have to write this: CBperipheral = [peripheral] to call my didConnectPeripheral after the call of CBmanager.connectPeripheral :)

Resources