Check Bluetooth status - Swift 4 - ios

I have a problem with the Bluetooth in Xcode. I can’t find a great solution on how to check if Bluetooth is on or not. I want just that. I searched around the web some solution, but nothing works for me. Any idea on how to check Bluetooth? I imported the CoreBluetooth class and I made this line of code:
if CBPeripheralManager.authorizationStatus() == .denied { code }
if CBPeripheralManager.authorizationStatus() == .authorized { code }

Implement CBCentralManagerDelegate delegate for that.
var manager:CBCentralManager!
viewDidLoad() { // Or init()
manager = CBCentralManager()
manager.delegate = self
}
Delegate method :
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
break
case .poweredOff:
print("Bluetooth is Off.")
break
case .resetting:
break
case .unauthorized:
break
case .unsupported:
break
case .unknown:
break
default:
break
}
}

you will need to use CBCentralManager and it provide delegate method "centralManagerDidUpdateState" https://developer.apple.com/documentation/corebluetooth/cbcentralmanager
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
if central.state == .poweredOn
{
print("Searching for BLE Devices")
// Scan for peripherals if BLE is turned on
}
else
{
// Can have different conditions for all states if needed - print generic message for now, i.e. Bluetooth isn't On
print("Bluetooth switched off or not initialized")
}
}

Related

iOS SWIFT - Detect peripheral turning off from central

Is there any way from the Central app to detect when a peripheral for example runs out of power and consequently disconnects?
I tried using this:
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { bleCentralManagerDelegate?.disconnectFromDeviceBLEResult(result: true)}
But this event gets called only if the peripheral requests an actual disconnection, and does NOT get called if the peripheral randomly turns off.
Thanks
Unfortunately there isn't. A generic BLE disconnection usually has the "disconnection reason" as part of the disconnection event (see this), but this is not reliable and in any case CoreBluetooth does not directly expose this. I say "not directly expose this" because you do get the error parameter as part of the event, but this is not a direct mapping to the actual disconnection reason happening on the stack.
The only workaround you can do is if you add the intelligence yourself. In other words, when the peripheral is about to turn off or when it has very low battery, it can send that information to the central (via a GATT write/notification) to let it know that it is about to disconnect because the battery is low.
Have a look at the links below for more information:-
Understanding BLE disconnections
CoreBluetooth CBError
Is this what you're looking for?
func centralManagerDidUpdateState(_ central: CBCentralManager) {
let cState = central.state
switch cState {
case .unknown:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .unknown")
}
case .resetting:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .resetting")
}
case .unsupported:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .unsupported")
}
case .unauthorized:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .unauthorized")
}
case .poweredOff:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .poweredOff")
}
case .poweredOn:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is .poweredOn")
}
centralManager.scanForPeripherals(withServices: servicesInterested)
#unknown default:
if ( preLogLevel == "LOGLEVEL" ) {
CSVfuncs.writeLog(">>BT central.state is Unknown Default")
}
break
// unknown default
}
}

Notifying iOS user that bluetooth must be turned on

I have an app that uses long-term BLE scanning in the background. I would like to detect when Bluetooth has been turned off so that I can send the user a notification saying that the apps functionality will be limited. Is this possible?
You can conform to CBCentralManagerDelegate and implement centralManagerDidUpdateState(_:) to be notified of the change in state
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
debugPrint("Scanner powered on")
break
case .poweredOff:
debugPrint("Scanner powered off")
break
case .resetting:
debugPrint("Resetting scanner")
break
case .unauthorized:
debugPrint("Unauthorized")
case .unknown:
debugPrint("unknown")
case .unsupported:
debugPrint("Scanner not supported")
}
}
Hope this helps

CBCentralManager iOS10 and iOS9

So I'm migrating to iOS10 but I also need my code to run on iOS9. I'm using CoreBluetooth and CBCentralManagerDelegate. I can get my code to work for iOS10 however I need the fallback to work for iOS9 as well.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if #available(iOS 10.0, *) {
switch central.state{
case CBManagerState.unauthorized:
print("This app is not authorised to use Bluetooth low energy")
case CBManagerState.poweredOff:
print("Bluetooth is currently powered off.")
case CBManagerState.poweredOn:
print("Bluetooth is currently powered on and available to use.")
default:break
}
} else {
// Fallback on earlier versions
switch central.state{
case CBCentralManagerState.unauthorized:
print("This app is not authorised to use Bluetooth low energy")
case CBCentralManagerState.poweredOff:
print("Bluetooth is currently powered off.")
case CBCentralManagerState.poweredOn:
print("Bluetooth is currently powered on and available to use.")
default:break
}
}
}
I get the error:
Enum case 'unauthorized' is not a member of type 'CBManagerState'
On the line:
case CBCentralManagerState.unauthorized:
As well as for .poweredOff and .poweredOn.
Any ideas how I can get it to work in both cases?
You can simply omit the enumeration type name and just use the .value. This will compile without warnings and works on iOS 10 and earlier since the underlying raw values are compatible.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state{
case .unauthorized:
print("This app is not authorised to use Bluetooth low energy")
case .poweredOff:
print("Bluetooth is currently powered off.")
case .poweredOn:
print("Bluetooth is currently powered on and available to use.")
default:break
}
}
I worked around this issue on Xcode 8 with Swift 2.3 (targeting iOS 8 and above) by creating an extension property on CBCentralManager which is of the old enum type, CBCentralManagerState. I named it centralManagerState. I refer to CBCentralManager.centralManagerState where I used to refer to CBCentralManager.state.
extension CBCentralManager {
internal var centralManagerState: CBCentralManagerState {
get {
return CBCentralManagerState(rawValue: state.rawValue) ?? .Unknown
}
}
}
I got the idea from this forum thread though they hadn't posted the code yet.
I contacted Apple about this and was given the following response (paraphrasing).
Due to the changing nature of swift, the above implementation is not possible however you can use the rawValue of the enum as the state is identical between the two classes. Therefore the following will work for now:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if #available(iOS 10.0, *) {
switch central.state{
case CBManagerState.unauthorized:
print("This app is not authorised to use Bluetooth low energy")
case CBManagerState.poweredOff:
print("Bluetooth is currently powered off.")
case CBManagerState.poweredOn:
print("Bluetooth is currently powered on and available to use.")
default:break
}
} else {
// Fallback on earlier versions
switch central.state.rawValue {
case 3: // CBCentralManagerState.unauthorized :
print("This app is not authorised to use Bluetooth low energy")
case 4: // CBCentralManagerState.poweredOff:
print("Bluetooth is currently powered off.")
case 5: //CBCentralManagerState.poweredOn:
print("Bluetooth is currently powered on and available to use.")
default:break
}
}
}
func centralManagerDidUpdateState(central: CBCentralManager)
{
if #available(iOS 10.0, *)
{
switch (central.state) {
case CBManagerState.PoweredOff:
print("CBCentralManagerState.PoweredOff")
case CBManagerState.Unauthorized:
// Indicate to user that the iOS device does not support BLE.
print("CBCentralManagerState.Unauthorized")
break
case CBManagerState.Unknown:
// Wait for another event
print("CBCentralManagerState.Unknown")
break
case CBManagerState.PoweredOn:
print("CBCentralManagerState.PoweredOn")
self.centralManager!.scanForPeripheralsWithServices([CBUUID(string:TRANSFER_UUID)], options:[CBCentralManagerScanOptionAllowDuplicatesKey: false])
case CBManagerState.Resetting:
print("CBCentralManagerState.Resetting")
case CBManagerState.Unsupported:
print("CBCentralManagerState.Unsupported")
break
}
}
else
{
switch central.state.rawValue
{
case 0: // CBCentralManagerState.Unknown
print("CBCentralManagerState.Unknown")
break
case 1: // CBCentralManagerState.Resetting
print("CBCentralManagerState.Resetting")
case 2:// CBCentralManagerState.Unsupported
print("CBCentralManagerState.Unsupported")
break
case 3: // CBCentralManagerState.unauthorized
print("This app is not authorised to use Bluetooth low energy")
break
case 4: // CBCentralManagerState.poweredOff:
print("Bluetooth is currently powered off.")
case 5: //CBCentralManagerState.poweredOn:
self.centralManager!.scanForPeripheralsWithServices([CBUUID(string:TRANSFER_UUID)], options:[CBCentralManagerScanOptionAllowDuplicatesKey: false])
print("Bluetooth is currently powered on and available to use.")
default:break
}
}
}

BLE Scanning at Start in Swift

I have scanning for a single UUID working in my Swift app when attached to an IBAction: UIButton. However, I'm now trying to get it to start scanning right when the app starts (background scanning should also be working as I have it set to a single UUID).
I've tried what I think is logical:
self.centralManager?.scanForPeripheralsWithServices(arrayOfServices, options: nil)
in viewDidLoad with arrayOfServices being the UUID of course. But this doesn't seem to work.
How do I get my app to look for peripherals upon start up without being prompted with a button press?
You will need to initialize the CentralManager and wait for the centralManagerDidUpdateState call via the CBCentralManagerDelegate. Once you verify that the State is PoweredOn, you can start scanning.
func centralManagerDidUpdateState(central: CBCentralManager) {
switch central.state
{
case CBCentralManagerState.PoweredOff:
print("Powered Off")
case CBCentralManagerState.PoweredOn:
print("Powered On")
//Start Scanning Here
self.centralManager?.scanForPeripheralsWithServices(arrayOfServices, options: nil)
case CBCentralManagerState.Unsupported,
CBCentralManagerState.Resetting,
CBCentralManagerState.Unauthorized,
CBCentralManagerState.Unknown:
print("Unexpected CBCentralManager State")
fallthrough
default:
break;
}
}
Swift 3
func centralManagerDidUpdateState(_ central: CBCentralManager){
switch central.state{
case .poweredOn:
print("Power On")
central.scanForPeripherals(withServices: nil, options: nil)
break
case .poweredOff:
print("Power Off")
break
case .unsupported:
print("Power Unsupported")
break
case .resetting:
print("Power Resetting")
break
case .unauthorized:
print("Power Unauthorized")
break
case .unknown:
print("Power Unknown")
break
}
}

iOS 8 Core Bluetooth not discovering peripherals

I'm having trouble getting Core Bluetooth to discover peripherals on iOS 8. Same code works fine on iOS 7 device. Initially I thought it would be a permissions issue since I had been doing some iBeacon work and there are some changes in Core Location permissions on iOS 8. I couldn't find anything online that helped with that however. Here is a link to a sample project that works fine for me on iOS 7 but not on iOS 8:
https://github.com/elgreco84/PeripheralScanning
If I run this project on an iOS 7 device it will log advertisement data for a number of devices around me. On iOS 8 the only output I see is that the Central Manager state is "Powered On".
It isn't valid to start scanning for peripherals until you are in the 'powered on' state. Perhaps on your iOS7 device you are lucky with timing, but the code is still incorrect. Your centralManagerDidUpdateState: should be
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStateUnsupported:
{
NSLog(#"State: Unsupported");
} break;
case CBCentralManagerStateUnauthorized:
{
NSLog(#"State: Unauthorized");
} break;
case CBCentralManagerStatePoweredOff:
{
NSLog(#"State: Powered Off");
} break;
case CBCentralManagerStatePoweredOn:
{
NSLog(#"State: Powered On");
[self.manager scanForPeripheralsWithServices:nil options:nil];
} break;
case CBCentralManagerStateUnknown:
{
NSLog(#"State: Unknown");
} break;
default:
{
}
}
}
And remove the call to scanForPeripheralsWithServices from didFinishLaunchingWithOptions
I ran into the same issue while building a very basic BLE scanner app. The required method "centralManagerDidUpdateState" was added. But nothing worked.
I believe the problem is related to queue.
Put the CBCentralManager instance in a dispatch_get_main_queue
This code snippet does that:
// BLE Stuff
let myCentralManager = CBCentralManager()
// Put CentralManager in the main queue
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
myCentralManager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
}
Using the Default Single View xCode start app. You can put this into the ViewController.swift file:
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
// BLE Stuff
let myCentralManager = CBCentralManager()
var peripheralArray = [CBPeripheral]() // create now empty array.
// Put CentralManager in the main queue
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
myCentralManager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark CBCentralManager Methods
func centralManagerDidUpdateState(central: CBCentralManager!) {
updateStatusLabel("centralManagerDidUpdateState")
switch central.state{
case .PoweredOn:
updateStatusLabel("poweredOn")
case .PoweredOff:
updateStatusLabel("Central State PoweredOFF")
case .Resetting:
updateStatusLabel("Central State Resetting")
case .Unauthorized:
updateStatusLabel("Central State Unauthorized")
case .Unknown:
updateStatusLabel("Central State Unknown")
case .Unsupported:
println("Central State Unsupported")
default:
println("Central State None Of The Above")
}
}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println("Did Discover Peripheral")
}
}

Resources