I have an iOS app which acts as a central that connects to an external peripheral via CoreBluetooth.
After I have connected to the peripheral, I read and write data to it and then disconnect. Upon disconnect, I try to establish a connection once again so the next time the peripheral goes into advertise mode, the app will connect to it like so:
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
central.connect(peripheral, options: nil)
// More actions here...
}
It works great when the app is in the foreground / background, but when terminated, it doesn't work at all.
I have defined the CBCentralManager with the CBCentralManagerOptionRestoreIdentifierKey like so:
self.central = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: Application.bluetoothRestoreIdentifier])
I have also implemented the willRestoreState central delegate method:
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any])
What am I doing wrong?
Thanks a lot!
Try with below code
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
let connectedperipherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral]
if let peripheral = connectedperipherals?.first {
central.connect(peripheral, options: nil)
}
}
Related
I'm trying to use corebluetooth to connect to a BLE device while in background mode. I already have have background mode working and currently receives disconnection callbacks while backgrounded.
I create the manager like this
centralManager = CBCentralManager(delegate: self, queue: nil,
options: [CBCentralManagerOptionRestoreIdentifierKey: "myCentralManagerIdentifier"])
I then implement the scan with
centralManager.scanForPeripherals(withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
then I connect to the device after the scan with
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
if peripheral.name == "My BLE" {
self.peripheral = peripheral
self.peripheral.delegate = self
centralManager.stopScan()
// Connect!
centralManager.connect(self.peripheral, options: nil)
}
}
Now when the app is backgrounded, and the BLE disconnect, I get this call back (which is good, it shows that background mode and state restoration is working)
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?)
{
print("Disconnected")
peripheral.delegate = self
centralManager.connect(peripheral, options: nil)
}
I immediately try to reconnect. But even when the device comes back online, I never receive any call back. I was under the impression that the way this API work is that the OS will attempt the connection for me while my app is in background and when the BLE is back online I will get notified?
I have the following delegate method to let me know when it is connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//send some user notifications and logs
alert()
}
didConnect is called when the first connection is made but is not called the second time when the BLE device comes backonline while in background
According to apple documentation
When the user leaves home, the iOS device may eventually become out of
range of the lock, causing the connection to the lock to be lost. At
this point, the app can simply call the connectPeripheral:options:
method of the CBCentralManager class, and because connection requests
do not time out, the iOS device will reconnect when the user returns
home.
In Swift, and Xcode 9, I am trying to scan for and connect to all BLE devices. This code should print out "no name" for all BLE devices found. Scanning is working, but my BLE devices are not being found. Beside me, there is an Android phone with BLE, which I am unable to find.
Simulation device: iPhone 6s, bluetooth turned -ON
import UIKit
import CoreBluetooth
//let svcLight = CBUUID.itit(string: "24958294582945")
class BLEViewController: UIViewController , CBCentralManagerDelegate, CBPeripheralDelegate{
func centralManagerDidUpdateState(_ central: CBCentralManager) {
//scan for peripherals if "on" state change
if central.state == CBManagerState.poweredOn{
//not concerned with any services for now, and we are not passing in any options
central.scanForPeripherals(withServices: nil, options: nil)
print("scanning...")
//check for other states, add if else statements
}
}
//handle the callback for when something is found
//diddiscover was the autocomplete word for a peripheral
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if peripheral.name?.contains("POR 1007BT") == true {
//even if the peripheral doesn't have a name, we will get some info for it
print (peripheral.name ?? "no name")
centralManager.stopScan()
print(advertisementData)
//connect to the peripheral now
central.connect(peripheral, options: nil)
//store a local copy of the peripheral in the property
myPeripheral = peripheral
}
}
//so our central can begin scanning again
//if peripheral disconnects for whatever reason, it will immidiately start scanning
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
central.scanForPeripherals(withServices: nil, options: nil)
}
//callback for connecting to a central, didconnect was the autocomplete word
//discovr services
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connected \(peripheral.name)")
peripheral.discoverServices(nil)
peripheral.delegate = self
}
//callback for diddiscover services: auto complete: diddiscover
//for each of the services in my peripheral, print out the UUID
//may not need this funciton, need to check
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
//optionally binding it
if let services = peripheral.services {
for svc in services {
print(svc.uuid.uuidString)
}
}
}
//! is used to unwrap it so anywhere in the code, it doesn't need to be unwrapped as an optional
var centralManager : CBCentralManager!
//keep a reference/store our peripheral
var myPeripheral : CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//create instance of CBManager
//pass in self as the delegate to handle any callbacks
centralManager = CBCentralManager.init(delegate: self, queue: nil)
}
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
I'm new to working with BLE, currently trying to make a simple application which would connect to my custom BLE device. I am able to discover the BLE device, but for some reason i can't connect to it.
I tried to check it with 'Light Blue', it shows my device as connectable and seems to work fine. But in my app after i discover the device, CB manager tries to connect to it and seems to 'freeze'? Function 'didConnect peripheral' is never triggered, and state of peripheral is forever 'connecting'.
How can i identify the problem? Is there any options i can include in connection method, or somehow track the connection process?
I would appreciate any advice where to search for problems.
Working in XCode 8.2.1, using Swift 3. iOS 10.2.1 installed on the testing phone
Here's my code:
import UIKit
import CoreBluetooth
class InfoPageViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager:CBCentralManager!
var peripheral:CBPeripheral!
let BEAN_NAME = "MyDevice"
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
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 {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connect(peripheral, options: nil)
print("discovered \(BEAN_NAME)")
}
}
func centralManager(
central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("connected to \(BEAN_NAME)")
peripheral.discoverServices(nil)
}
func centralManager(central: CBCentralManager, didConnect peripheral: CBPeripheral) {}
--------------------^
Versus:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
--------------------^
The signature of the method is not the correct one, you are missing the _.
Method signatures are important.
We can assume, since theses delegate methods are optional, that internally, the Apple code asks itself:
Does my delegate have the method func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) implemented (respondsToSelector:)? In your case, not, because it's not the same, and then yours is not called.
You copy/paste the one from the doc or remove it and let XCode do its autocompletion thing.
I am trying to connect to my Arduino project that is using the BlueFruit BLE spi module. I am having an issue when trying to connect using my iOS app. After I have found the device I try to connect to it but the state gets stuck in 'connecting' state=1. This prevent me from searching the services and such because a 'connected' state isn't achieved
Here is a code snip...
//check state of the bluetooth on phone
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOff{
//TODO: ADD SAVE DATA TO REALM BEFORE DISSMISSING
errorView.isHidden = false
}
if central.state == .poweredOn{
errorView.isHidden = true
//scan for peripherals with the service i created
central.scanForPeripherals(withServices: nil, options: nil)
}
}
//devices found(should only be ours because we will create Unique serviceID)
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// get advertisement data and check to make sure the name is matching. set it as the peripheral then make connection
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
print("NEXT PERIPHERAL NAME: \(peripheralName)")
print("NEXT PERIPHERAL UUID: \(peripheral.identifier.uuidString)")
if peripheralName == nameID{
manager.stopScan()
self.peripheralHalo = peripheral
peripheralHalo!.delegate = self
manager.connect(peripheral, options: nil)
while(peripheralHalo?.state.rawValue == 1)
{
if(manager.retrieveConnectedPeripherals(withServices: [serviceID]).count > 0 ){
print("\(manager.retrieveConnectedPeripherals(withServices: [serviceID]))")
}
}
}
print("Connected!!")
}
When I call manager.connect(peripheral, options: nil) , the peripheral tries to connect. I add the following while loop for testing and always shows the state as "connecting". I have tried the LightBlue iOS app and i can properly connect and receive notifications of characteristic value changes so the Arduino firmware should be all good.PLEASE HELP!!!
You don't want that while loop; This will just block the Core Bluetooth delegate thread. After issuing the connect you will get a call to the didConnect CBCentralManagerDelegate method. Once the peripheral is connected you need to call discoverServices on the peripheral, which will give a callback to the peripheral:didDiscoverServices: peripheral delegate method. You can then discover the characteristics in a similar way.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// get advertisement data and check to make sure the name is matching. set it as the peripheral then make connection
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
print("NEXT PERIPHERAL NAME: \(peripheralName)")
print("NEXT PERIPHERAL UUID: \(peripheral.identifier.uuidString)")
if peripheralName == nameID {
self.peripheralHalo = peripheral
central.stopScan()
central.connect(peripheral, options: nil)
}
}
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connected!!")
peripheralHalo!.delegate = self
peripheral.discoverServices([serviceID)
}
Also, if you are going to store something that identifies which peripheral you want to connect to, I suggest you use the identifier and not the name as the name can change.