CBCentralManager iOS10 and iOS9 - ios

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
}
}
}

Related

IOS vs OSX BLE: Instantiating CBCentralManager - coding for iOS

I'm learning iOS vs OSX BLE.
I notice that I can't instantiate CBCentralManager in iOS because of:
[CoreBluetooth] XPC connection invalid
Unsupported
Versus via OSX because the iOS platform doesn't have the 'App Sandbox' Characteristic where I can set for BLE use.
Here's my iOS Code:
import SwiftUI
struct ContentView: View {
#ObservedObject var bleManager = BLEManager()
var body: some View {
ZStack {
Color("Background")
Text("Hello")
}
}
}
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("power is on")
case .resetting:
print("Resetting")
case .unsupported:
print("Unsupported")
case .unauthorized:
print("UnAuthorized")
case .unknown:
print("UnKnown")
case .poweredOff:
print("Powered OFF")
#unknown default:
print("**** Default ****")
}
}
}
Here's the required .plist entry:
I understand that iPhones can be either a BLE center or peripheral.
Simple Question: How do I code for a bona fide CBCentralMaster for iOS?
I'm merely taking baby steps here: coding for peripheral detection.
... then continue from there.
Are you running on the simulator (where it is unsupported) instead of on device?
See the main Core Bluetooth docs:
Your app will crash if its Info.plist doesn’t include usage description keys for the types of data it needs to access. To access Core Bluetooth APIs on apps linked on or after iOS 13, include the NSBluetoothAlwaysUsageDescription key. In iOS 12 and earlier, include NSBluetoothPeripheralUsageDescription to access Bluetooth peripheral data.

how to turn on bluetooth force fully or how to ask permission for turn on bluetooth in my ios application

func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state)
{
case .PoweredOff:
print("off")
case .PoweredOn:
centralManager.scanForPeripheralsWithServices(nil, options: nil)
case .Resetting:
print("resetting")
case .Unauthorized:
print("unaithorized")
case .Unsupported:
print("unsupported")
case .Unknown:
print("unknown")
}
}
this code only check the status of bluetooth it can not turn on i want to turn on bluetooth at any way

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

Check Bluetooth status - Swift 4

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")
}
}

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
}
}

Resources