I'm trying to send a process in a background thread using the following code:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
print("running in the background queue")
btDiscovery
})
but the class is only processing while begin in foreground...any idea ?
EDIT1:
btDiscovery is a class which performs a BLE device scan every X seconds:
let btDiscoverySharedInstance = Beacon();
class Beacon: NSObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager?
private var peripheralBLE: CBPeripheral?
....
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
print("BLE powered off")
self.clearDevices()
case CBCentralManagerState.Unauthorized:
// Indicate to user that the iOS device does not support BLE.
print("BLE not supported")
break
case CBCentralManagerState.Unknown:
// Wait for another event
print("BLE unknown event")
break
case CBCentralManagerState.PoweredOn:
print("BLE powered on")
self.startScanning()
break
case CBCentralManagerState.Resetting:
print("BLE reset")
self.clearDevices()
case CBCentralManagerState.Unsupported:
print("BLE unsupported event")
break
}
}
func startScanning() {
print("Start scanning...")
if let central = centralManager {
central.scanForPeripheralsWithServices(nil, options: nil)
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered peripheral \(RSSI) dBM name: \(peripheral.name)")
print("UUID: \(peripheral.identifier.UUIDString)")
...
sleep(delayPolling)
self.startScanning()
}
when the app is launched and remains in foreground, the scan is performed correctly every "delayPolling" seconds.
but as soon as I put my app is background, the scan is paused. it restarts only when it comes back again in foreground.
I would need to leave this scan running in background every time (even if we set a lower priority to this thread).
EDIT2:
by reading the documentation https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
I can see that
When an app that implements the central role includes the UIBackgroundModes key with the bluetooth-central value in its Info.plist file, the Core Bluetooth framework allows your app to run in the background to perform certain Bluetooth-related tasks. While your app is in the background you can still discover and connect to peripherals, and explore and interact with peripheral data. In addition, the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked
I selected the corresponding options in my Info.plist file:
but my app is not running my thread in background.
I realize this is an old question, but scanning in the background requires that you supply a Service UUID.
central.scanForPeripheralsWithServices(nil, options: nil)
needs to be
central.scanForPeripheralsWithServices(serviceUUID, options: nil)
Related
I have RFID reader, which is not LE device.
https://www.tsl.com/products/1153-bluetooth-wearable-uhf-rfid-reader
I'm trying to write an iOS application, scan this device and connect it using swift CoreBluetooth library but my App finds everything besides this device. How is it possible to scan this reader?
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
break;
case .poweredOff:
break;
case .poweredOn:
manager.scanForPeripherals(withServices: nil)
break;
case .resetting:
break;
case .unauthorized:
break;
case .unsupported:
break;
default:
break;
}
}
}
That device states that is MFi certified and uses the SPP profile, not the BLE GATT profile. This means that you will need to use the External Accessory Framework, not Core Bluetooth, to communicate with it.
You will need to the manufacturer provided iOS SDK for the device. If they do and you want to release your app on the App Store then they will also need to approve your app and supply some paperwork to Apple.
The device says that it also supports the HID profile, so perhaps you could just treat it as a keyboard; This doesn't require any code but isn't the best user experience.
You need to use CoreNFC to read RFID tags. No need to use CoreBluetooth at all.
https://developer.apple.com/documentation/corenfc
I am currently writing an app that connects via BLE to an external device. All operations are fine when the app is in foreground.....including connecting, obtaining data, and reconnecting (in cases of the device going out of range) via a reconnect protocol I wrote. The app also functions properly when it is backgrounded but the BLE connection remains alive.
However, the only instance in which the app does not function is if the app is backgrounded and then the BLE device goes out of range. Once the connection is broken, the app seems to be suspended by iOS after a few seconds and none of the code I wrote will continue to function...even if I bring the device back into range. The only way to restore functionality is to bring the app back into the foreground again. (Note: I have the info.plist file and all other settings configured appropriately for centralManager background functionality)
I've read some documentation and it seems that this comes down to not having state preservation/restore code properly implemented. I went ahead and implemented the "willRestoreState" and "didUpdateState" commands, but the app still doesn't reconnect to a device once it has been suspended when in background mode.
I've shown some relevant code below, including the willRestoreState, didUpdateState, and didDisconnect methods. Any ideas or suggestions? Thanks!
//define service+characteristic UUIDS
let serviceUUID = CBUUID(string: "xxxxxxxxx")
let streamingCharacteristicUUID = CBUUID(string: "xxxxxxxxx")
//Local dictionary of UUIDs for connected devices (the ble code updates this var with each connected device)
var devicesUniqueId:[UUID:String] = [UUID:String]()
//Local dictionary of connected peripherals, with respect to each of their UUIDS (the ble code updates this var with each connected device)
var sensorPeripheral = [UUID:CBPeripheral]()
///restoreState function
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
if let peripheralsObject = dict[CBCentralManagerRestoredStatePeripheralsKey] {
let peripherals = peripheralsObject as! Array<CBPeripheral>
print ("starting restorestate code")
if peripherals.count > 0 {
for i in 0 ..< peripherals.count {
print ("starting restorecheck")
//Check if the peripheral exists within our list of connected peripherals, and assign delegate if it does
if self.devicesUniqueId.keys.contains(peripherals[i].identifier) {
peripherals[i].delegate = self
}
}
}
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
if central.state != .poweredOn
{
return
}
self.startScanning()
//////Preservation + Restoration code////////
//Iterate through array of connected UUIDS
let keysArray = Array(self.patchDevicesUniqueId.keys)
for i in 0..<keysArray.count {
//Check if peripheral exists for given UUID
if let peripheral = self.sensorPeripheral[keysArray[i]] {
print("peripheral exists")
//Check if services exist within the peripheral
if let services = peripheral.services {
print("services exist")
//Check if predefined serviceUUID exists within services
if let serviceIndex = services.index(where: {$0.uuid == serviceUUID}) {
print("serviceUUID exists within services")
let transferService = services[serviceIndex]
let characteristicUUID = streamingCharacteristicUUID
//Check if predefined characteristicUUID exists within serviceUUID
if let characteristics = transferService.characteristics {
print("characteristics exist within serviceUUID")
if let characteristicIndex = characteristics.index(where: {$0.uuid == characteristicUUID}) {
print("characteristcUUID exists within serviceUUID")
let characteristic = characteristics[characteristicIndex]
//If characteristicUUID exists, begin getting notifications from it
if !characteristic.isNotifying {
print("subscribe if not notifying already")
peripheral.setNotifyValue(true, for: characteristic)
}
else {
print("invoke discover characteristics")
peripheral.discoverCharacteristics([characteristicUUID], for: transferService)
}
}
}
}
else {
print("invoke discover characteristics")
peripheral.discoverServices([serviceUUID])
}
}
}
}
}
//didDisconnect method to handle a connect command issue
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
{
//commented out unnecessary code
self.removePeripheralData(peripheral: peripheral)
if(sensorCount>0){
sensorCount -= 1
}
}
//removePeripheralData function used in didDisconnect
func removePeripheralData ( peripheral: CBPeripheral) {
//Commented out unnecessary code
//Issue reconnect command
print ("issuing reconnect command")
centralManager.connect(peripheral, options: nil)
//Commented out unnecessary code
handleDidRemoveDevice()
}
Now I'm currently doing an application project that needs my iPhone to scan other nearby bluetooth devices and list them out. I'm wondering is my code has any problem?
Code:
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager (delegate: self, queue: nil)
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Peripheral: \(peripheral)")
}
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.scanForPeripheralsWithServices(nil, options: nil)
default:
print("default state")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm using iPhone 5 (iOS 9) and I'm sure that my Bluetooth is turned on.
When I run the application in my iPhone, the console only log the following output:
Checking
BLE service is powered on
Start Scanning
But there is no Bluetooth device's name shown in the output. Even I turn on my iPad (iPad Mini 4 iOS 8) and the list still wouldn't update.
Sometimes it does scan my MacBook Pro Bluetooth and the output will have this:
Peripheral: <CBPeripheral: 0x14d70e00, identifier = 54738076-6C97-FD04-18CF-5E1AF6705865, name = vivien’s MacBook Pro, state = disconnected>
So, why is this happening? Can someone please explain to me?
case 1:
You must use GKSession to scan and connect with another iOS device,not CoreBluetooth.
case 2:
Your bluetooth device is a Bluetooth 3.0 accessory.Your iPhone can discover and show it in Setting->Bluetooth.
But this message isn't delivered to your app,so your app won't discover it.
Try again with a Bluetooth 4.0 accessory.
I am new to swift language. I have been working on establishing a bluetooth connection between my iOS app and a barcode scanner. The barcode scanner has bluetooth enabled. I tried to establish a global bluetooth connection with my iphone and it works. Based on some references from the Internet, I have written the following sample code.
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager : CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate : self, queue : nil)
}
func centralManagerDidUpdateState(central: CBCentralManager) {
var consoleMsg = "hello"
switch(central.state) {
case .PoweredOff:
consoleMsg = "Bluetooth is powered off"
case .PoweredOn:
consoleMsg = "Bluetooth is powered on"
manager.scanForPeripheralsWithServices(nil, options: nil)
case .Resetting:
consoleMsg = "Bluetooth is Resetting"
case .Unauthorized:
consoleMsg = "Bluetooth is Unauthorized"
case .Unknown:
consoleMsg = "Bluetooth is Unknown"
case .Unsupported:
consoleMsg = "Bluetooth is Unsupported"
}
print("\(consoleMsg)")
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered a peripheral \(peripheral)")
}
When I try to run the above code, log displays "Bluetooth is Powered on" and thats it. It is not discovering my barcode scanner. I also made sure that the barcode scanner is in discoverable mode. Why my code is not discovering the near-by bluetooth enabled barcode scanner? Have I done any mistake in the above code?
Thanks
You should absolutely check authorization first !
I'm trying to listen to all Bluetooth connect/disconnect events. Even though the delegate's centralManagerDidUpdateState function is called, nothing happens when I connect or disconnect Bluetooth devices.
I'm confused as to what's going wrong. I initialize the Central Manager/delegate like this:
var btDelegate: CBCentralManagerDelegate = BluetoothDelegate()
var btManager = CBCentralManager(delegate: btDelegate, queue: nil)
BluetoothDelegate is defined like so:
import Foundation
import CoreBluetooth
class BluetoothDelegate : NSObject, CBCentralManagerDelegate {
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
println("connect") //this line is not called
}
func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
println("disconnect") //this line is not called
}
func centralManagerDidUpdateState(central: CBCentralManager!) {
println("state update") //this line is called
}
}
Note: I can continuously receive more state update events such as when I toggle Bluetooth, even though connect and disconnect are not called.
From your code it looks like you haven't started scanning for peripherals. Once you have confirmed that your central is in powered on state from centralManagerDidUpdateState method you should start scanning for your peripherals.
(The bluetooth devices you connected from bluetooth settings panel are irrelevant. You can't have access to them. (as far as I know) Incase you want to scan and find out your device on your own below code will help)
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
break
case CBCentralManagerState.PoweredOn:
startScan() // start scanning once the bluetooth is On
break
default:
break
}
}
And your startScan method will be (You can provide service UUID, use nil if you want all )
func startScan(){
if let central = btManager {
central.scanForPeripheralsWithServices(nil, options: nil)
println("started Scanning")
}
}
After that whenever you discover a peripheral didDiscoverPeripheral method will be called first.
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println(peripheral)
// btManager.connectPeripheral(peripheral, options: nil)
}
From there you collect the peripheral and then invoke connectPeripheral method of CBCentralManager. If the connection is successful didConnectPeripheral method will be called.
You should go through this documentation for complete details