didDisconnectPeripheral not called iOS Swift 3 - ios

I'm trying to disconnect from a BLE device. I'm doing cancelPeripheralConnection() and its never calling didDisconnectPeripheral().
I'm setting notify value to one characteristic, so while disconnecting i'm setting it false.
{
BLEOperations.sharedInstance.connectedDevice?.setNotifyValue(false, for: BLEOperations.sharedInstance.peripheralCharacteristics[0])
BLEOperations.sharedInstance.manager.cancelPeripheralConnection(BLEOperations.sharedInstance.connectedDevice!)
}
I'm trying to disconnect connectedPeripheral when user tries to connect to new device. The current device is not disconnected but new device is connected along with old one.
{
BLEOperations.sharedInstance.manager.connect(self.peripheralArray[indexPath.row] as! CBPeripheral , options: nil)
}
What am i doing wrong here? When device connection is lost the method is being called.

It will work fine when the CentralManager has only one instance in the entire flow.
Instead of using centralmanagerdelegates in viewcontroller, you can create a CentralManager operations class (which is a singleton) and perform operations.
I did that and its working fine.

Well, probably we would need more information about your code. But the method you are looking for, is this one.
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
disconnected(central: central)
}
You should set your centralManager's delegate to self, and everytime a peripheral is disconnected it should call the delegate.
Doing some tests, the disconnection process is not as nice as it seems, you can see your peripheral is still disconnecting but the delegate has been called (this can be adjusted with the hardware itself or the firmware on your peripheral).
BTW, you can connect several devices at once.

Cancels an active or pending connection to peripheral
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
var peripherals: [CBPeripheral]?
#IBAction func actionScanBLEDevice(_ sender: AnyObject) {
manager = CBCentralManager (delegate: self, queue: nil)
}
#IBAction func actionDisconnectBLE(_ sender: AnyObject) {
//Use As per need where you want to disconnect perticular peripheral
manager.cancelPeripheralConnection(peripheral[0]) // Disconnent numbers of BLE Devices
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
//Manage Some Condition then disconnect
print("Disconnected from peripheral")
peripherals?.append(peripheral)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Discovered peripheral \(peripheral.name,peripheral.identifier.uuidString)")
print("advertisementData\(advertisementData)")
// Use discovered peripheral here
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("Checking")
switch(central.state)
{
case.unsupported:
print("BLE is not supported")
case.unauthorized:
print("BLE is unauthorized")
case.unknown:
print("BLE is Unknown")
case.resetting:
print("BLE is Resetting")
case.poweredOff:
print("BLE service is powered off")
case.poweredOn:
print("BLE service is powered on")
print("Start Scanning")
manager.scanForPeripherals(withServices: nil, options: nil)
}
}
}

Related

iPadOS 13.4 Ble pairing popup triggered without read/write to protected characteristic

Pre iPadOS 13.4 we needed to read/write to protected characteristic of a peripheral device in order to trigger pairing popup. Starting iPadOS 13.4 - the passcode popup seems to be triggered simply via a successful connection with the peripheral (CBCentralManager().connect(peripheral, options: nil)).
I need to further communicate with the peripheral in order to get the passcode before the pairing popup is displayed. Once the pairing popup is displayed - the peripheral stops responding to any further requests.
Is this a design change or a bug on 13.4? I cannot find anything on the web/apple's release notes for iPadOS 13.4.
If this is a design change - what is an elegant way of handling this?
The following code triggers pairing on didConnect peripheral:
//Sample Code
var centralManager: CBCentralManager?
var peripheral: CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("CentralManager state: Powered On")
centralManager?.scanForPeripherals(withServices: [advertisingUUID], options: nil)
print("Scanning for peripherals...")
default:
print("CentralManager state: Powered Off")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.peripheral = peripheral
self.peripheral?.delegate = self
centralManager?.connect(peripheral, options: nil)
centralManager?.stopScan()
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Peripheral Connected")
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?){
print("Peripheral Disconnected")
}

Can bluetooth trigger a certain action in an app?

I am planning to start an ios application and was wondering if there is a way to trigger certain action such as finding location using google map on bluetooth connection (or disconnection)?
I know an apple map application has similar feature where it shows where your car is parked. Any references would be appreiciate.
Thanks.
You can try this in your viewController where you want to check:
var manager: CBCentralManager!
override func viewDidLoad(){
super.viewDidLoad()
manager = CBCentralManager()
manager.delegate = self
}
//CBCentralManager Delegate method
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
//poweredOn
case .resetting:
//resetting
case .unauthorized:
//unauthorized
case .unknown:
//unknown
case .unsupported:
//UnSupported
case .poweredOff:
//Bluetooth is not Connected.Please Enable it
}
the status in delegate will tell you what's the status of your Bluetooth.
Hope it helps!
Well there delegate methods which get fired after different states of connection. The best way I suggest you to do is implement the following delegate methods.
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
}
And call a function to get your location in these cases. Additionally there are other delegate methods such as
func centralManagerDidUpdateState(_ central: CBCentralManager) {
}
func centralManager(_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?) {
}
But these cases does not cover the all the cases of connection and disconnection. But depending on your requirement you can implement either one. These are CBCentralManagerDelegate delegates.
CBCentralManager
CBCentralManagerDelegate

Bluetooth didConnect or didFailToConnect not called Xcode 9.2 with iOS 11.1.2

I am having the problem with Xcode 9.2 and iOS 11.1.2,"didDiscover" is working fine and my peripheral is saved in an array before I call connect, but "didConnect" or "didFailToConnect" not called, so the peripheral state will stay at "connecting"...
Please help
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
//manager = CBCentralManager (delegate: self, queue: DispatchQueue.main)
//manager = CBCentralManager.init(delegate: self, queue: nil, options:[:])
//manager.delegate = self
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let name = peripheral.name else { return }
print("BR peripheral.name = \(name), rssi = \(RSSI), adertisementData = \(advertisementData)")
if (peripheral.name?.hasPrefix("testBT"))! {
peripheralArray.append(peripheral)
manager.connect(peripheralArray.first!, options: [:])
print("Connecting to peripheral \(peripheral)")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("BT connected!")
manager.stopScan()
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("BT disconnected!")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("BT failed!")
}
Try making variable global:
var myPeripheral:CBPeripheral?
Than, in your code edit it:
if (peripheral.name?.hasPrefix("testBT"))! {
myPeripheral = peripheral
//HERE WRITE DELEGATE, NOT IN didConnect
myPeripheral!.delegate = self
//peripheralArray.append(myPeripheral)
manager.connect(myPeripheral!, options: nil)
print("Connecting to peripheral \(peripheral)")
}
Now I think it should work. I would also start scanning on centralManagerDidUpdateState to make sure bluetooth is working, smth like:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state{
case .poweredOn:
startScan() // own method where i start scanning
case .poweredOff:
print("blth powered off")
case .unsupported:
print("bltyh noit supported")
}
}
etc. there is more states, you can check it in documentation. The centralManagerDidUpdateState method is beeing called after initializing in vievDidLoad your CBCentralManager var (manager = CBCentralManager(delegate: self, queue: nil))
Hope it helps
BTW, you are making an array to store there connected peripherals. Just have in mind that whenever you return from the controller where you store that array and go back to it that array will be empty again. That's why if you need your bluetooth class working globally I would suggest you to make a BluetoothService class that would have a Singleton implemented and you could access any state anywhere in your project

Bluetooth doesn't discover devices

First off, I know this question has been asked a lot on SO. And I went through all of them trying to resolve it but to no avail.
I have a simple app that scans for Bluetooth enabled devices. Here's my code.
import CoreBluetooth
import UIKit
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager: CBCentralManager!
var peripheral: CBPeripheral!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(central: CBCentralManager) {
print(#function)
switch central.state {
case .Unsupported:
print("Unsupported")
case .Unauthorized:
print("Unauthorized")
case .PoweredOn:
print("Powered On")
central.scanForPeripheralsWithServices(nil, options: nil)
case .Resetting:
print("Resetting")
case .PoweredOff:
print("Powered Off")
case .Unknown:
print("Unknown")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print(#function)
print("Discovered \(peripheral.name) at \(RSSI)")
if peripheral.name!.containsString(name) {
manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connectPeripheral(peripheral, options: nil)
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print(#function)
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
central.scanForPeripheralsWithServices(nil, options: nil)
}
// MARK: - CBPeripheralDelegate
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print(#function)
guard let services = peripheral.services else {
return
}
for service in services {
print(service.UUID)
if service.UUID == serviceUUID {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print(#function)
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
print(characteristic.UUID)
if characteristic.UUID == scratchUUID {
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(#function)
var count: UInt32 = 0
if characteristic.UUID == scratchUUID {
characteristic.value!.getBytes(&count, length: sizeof(UInt32))
print(String(format: "%llu", count))
}
}
}
I ran it in my iPhone 5 (iOS 9.3.4). I had a laptop running Windows, an Android phone, an iPad Air 2 and my Mac all Bluetooth turned on. But none of those devices were discovered. centralManagerDidUpdateState method gets called and scanning starts but that's it. didDiscoverPeripheral delegate method never gets called.
I repeated the process, this time ran it in the iPad Air 2 but same result.
If I go to the Bluetooth menu on the device I do see other devices being discovered.
In some SO answers, I saw that running it in the main queue would work. Like so, manager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue()). But that did not work for me either.
I'd really appreciate any help on this.
Your other devices are probably not advertising a BLE service (like Paulw11 mentioned in the comments). All of the devices you mentioned will not appear as discoverable unless they are in the Bluetooth Settings page.
There are apps for iOS, Android, and Windows that have them advertise over LE. The easiest to use is LightBlue for iOS. Run your app on the iPhone while the other iOS device is advertising and you should see the advertisements. It might be useful to get an LE advertisement scanner on Windows or Android to double check that LightBlue is advertising.
Note Windows does not yet support Peripheral Role for Bluetooth LE,
so even though you will be able to see the advertisement you will not
be able to connect to it.

Can I turn my new 4th gen AppleTV into an iBeacon?

I read you can write apps for the new AppleTV. I read it runs a version of iOS. I read too that it has bluetooth capabilities.
Question, very simply, could I turn my AppleTV into an iBeacon, albeit a very expensive one? :)
The way you make an iOS device broadcast is, you create a CLBeaconRegion object with the UUID, major and minor you want to broadcast, call its peripheralDataWithMeasuredPower method, and pass the dictionary you obtain this way to CBPeripheralManager's startAdvertising method.
Now, tvOS lacks the CLBeaconRegion class, but has the CBPeripheralManager complete with the startAdvertising method. This means you should be able to generate the dictionary to pass to startAdvertising on an iOS device, take a loot at its content, and replicate it in your tvOS app.
In fact, people have already been doing it with OS X Mavericks in the past (I think Apple blocked this in Yosemite): http://www.blendedcocoa.com/blog/2013/11/02/mavericks-as-an-ibeacon/
Note: I haven't tried this method myself. It's entirely possible Apple blocked this trick on tvOS just liked they did on OS X.
Seeing as people "jailbreak" (or outright install Linux on) Apple TVs, you can install pretty much anything. Since iBeacon is a standard Apple opened (and for which there is open source code), I'd say the answer is "yes".
Ok, I got around to testing this at last, with the result ok, some promise. Here is the details, tvOS doesn't have CLRegion, will not compile, so the official route for iBeacon don't work.
Did however get it to see the iBeacon with this code; so there is hope... maybe not with iBeacons, but Bluetooth...
This is the BLE code I used...
import Foundation
import CoreBluetooth
class BLEManager {
var centralManager : CBCentralManager!
var bleHandler : BLEHandler // delegate
required init() {
self.bleHandler = BLEHandler()
self.centralManager = CBCentralManager(delegate: self.bleHandler, queue: dispatch_get_main_queue())
}
}
class BLEHandler : NSObject, CBCentralManagerDelegate {
override init() {
super.init()
}
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state)
{
case .Unsupported:
print("BLE is unsupported")
case .Unauthorized:
print("BLE is unauthorized")
case .Unknown:
print("BLE is unknown")
case .Resetting:
print("BLE is reseting")
case .PoweredOff:
print("BLE is powered off")
case .PoweredOn:
print("BLE is powered on")
central.scanForPeripheralsWithServices(nil, options: nil)
default:
print("BLE default")
}
print("centralManagerDidUpdateState")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("didConnectPeripheral")
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("\(peripheral.name) : \(RSSI) dBm")
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("didDiscoverServices")
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print("didDiscoverCharacteristicsForService")
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print("didUpdateValueForCharacteristic")
}
func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print("didUpdateNotificationStateForCharacteristic")
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("didDisconnectPeripheral")
}
}
With these lines in the viewontroller
var bleManager: BLEManager!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
bleManager = BLEManager()
Need to say a special thank you to rhythmic-fistman who helped me with the code!

Resources