I am trying to connect with BLE in background but it did not connect in background.
It is working when my app is in foreground.
I am trying to scan with UUID of the peripheral.
Here is the attached code.
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var msg = ""
switch central.state {
case .poweredOff:
msg = "Bluetooth is Off"
case .poweredOn:
msg = "Bluetooth is On"
let arrayOfServices: [CBUUID] = [CBUUID(string: "CCAExxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
manager?.scanForPeripherals(withServices:arrayOfServices, options: nil)
case .unsupported:
msg = "Not Supported"
default:
msg = "Not Connected"
}
print("STATE: " + msg)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Name: \(peripheral.name)") //print the names of all peripherals connected.
//you are going to use the name here down here ⇩
if peripheral.name == "Name of device" { //if is it my peripheral, then connect
self.myBluetoothPeripheral = peripheral //save peripheral
self.myBluetoothPeripheral.delegate = self
manager.stopScan() //stop scanning for peripherals
manager.connect(myBluetoothPeripheral, options: nil) //connect to my peripheral
}
}
How can resolve it?
Turn on 'Background Modes' Capabilities.
And turn on below two option:
1. User Bluetooth LE accessories.
2. Act as Bluetooth LE accessory.
Do same as Screenshot:
Now BLE is working in background mode in iOS application.
What you have to do is when you instantiate CentralManager you need to instantiate it with a restoration identifier.
Ex:
CBCentralManager(delegate: self,options:
[CBCentralManagerOptionRestoreIdentifierKey: "bleCentralManager"])
This is necessary as in the apple documentation it says "Core Bluetooth preserves the state of only those objects that have a restoration identifier".
Then when your app is relaunched into the background you have to reinstantiate your appropriate central manager with the same restoration identifier inside you appDelegate's application:didFinishLaunchingWithOptions: method.You can get the restoration identifiers like this:
let centralManagerIdentifiers = launchOptions![UIApplicationLaunchOptionsKey.bluetoothCentrals]
Finally in your central managers centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) delegate method you can get a list of all the peripherals the central manager was connected to or was trying to connect to and do whatever want to do in this method.
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
}
Related
I just tried a sample code to find the list of all bluetooth devices that are connected or discoverable.
class ViewController: UIViewController, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
NSLog("Update state \(central.state)")
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices: nil, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
NSLog("Peripheral \(peripheral)")
}
}
The problem is it simply does not show all bluetooth devices that I can see in the Bluetooth menu in Settings app. Moreover, I can't even see device that is already connected to iPhone. What I am doing wrong?
I would ideally like to see all headphone & microphone accessories, not others. Not sure which serviceUUID values I can use and if there is any standard for those values.
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)
}
}
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'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)")
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.