I need to check if Bluetooth is On.
I use this code:
func startDetectingBluetoothState() {
if self.centralManager == nil {
self.centralManager = CBCentralManager(delegate: self, queue: self.workingQueue, options: [CBCentralManagerOptionShowPowerAlertKey: false])
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
let state = central.state
DispatchQueue.main.async { [weak self] in
// notify observers about state change
self?.stateObservers.invokeDelegates { stateObserver in
stateObserver.bluetoothStateChanged()
}
}
}
I start the app on iPhone X 11.3 (Bluetooth is On in the Settings), then almost immidiatly it enters in centralManagerDidUpdateState(_ central: CBCentralManager) with state == .poweredOff. So the value is wrong. Then I turn off Bluetooth in Settings, centralManagerDidUpdateState(_ central: CBCentralManager) function is not called (because the previous state it detected was .poweredOff), then I turn on Bluetooth and centralManagerDidUpdateState(_ central: CBCentralManager) is called with .poweredOn value. Then it detects every state change properly. But if I restart the app and Bluetooth is On, it can't detect it again. If during start the Bluetooth is Off, everything is OK.
I have another device iPhone 6+ 11.2.5, where everything works as a charm :)
You need to implement CBCentralManagerDelegate. Below is the sample code which might help you to check the condition -
var centralManager:CBCentralManager!
in init() or viewDidLoad()
{
centralManager = CBCentralManager()
centralManager.delegate = self
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Bluetooth is connected")
}
else if central.state == .poweredOff{
print("Bluetooth is not Connected.")
}
}
Use this Code Working for you
var centralManager:CBCentralManager!
override func viewDidLoad()
{
super.viewDidLoad()
// This will trigger centralManagerDidUpdateState
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
print("Central Manager did update state")
if (central.state == .poweredOn)
{
self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
}
else
{
// Bluetooth is unavailable for some reason
// Give feedback
var message = String()
switch central.state
{
case .unsupported:
message = "Bluetooth is unsupported"
case .unknown:
message = "Bluetooth state is unkown"
case .unauthorized:
message = "Bluetooth is unauthorized"
case .poweredOff:
message = "Bluetooth is powered off"
default:
break
}
print(message)
UIAlertView(
title: "Bluetooth unavailable",
message: message,
delegate: nil,
cancelButtonTitle: "OK")
.show()
}
}
Related
I have implemented CBManagerDelegate and can print the status successfully, thanks to this answer . But I want to store this state in a variable and use in my post API. How to proceed?
Tried to store in a variable normally but failed. Protocols might work but I am not sure.
class ViewController: UIViewController, CBCentralManagerDelegate {
var isBTTurnedOn: Bool?
var manager: CBCentralManager!
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("Bluetooth is ON.")
isBTTurnedOn = true
break
case .poweredOff:
print("Bluetooth is Off.")
isBTTurnedOn = false
#unknown default:
break
}
}
//IN THE VIEWDIDLOAD() ->
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager()
manager.delegate = self
}
// IN VIEWWILLAPPEAR()
print("Bt status is \(isBTTurnedOn)")) // Getting nil here
Is there any way in iOS to detect if Wifi & Bluetooth is off so that I can show alert to the user to enable it from settings.
Note: I am interested in the hardware setting is on/off not the internet is reachable or not.
You can try the following for network connection availability
func isInternetAvailable() -> Bool {
var isNetworkReachable = false
if let reachability: Reachability = try? Reachability() {
isNetworkReachable = reachability.connection != .unavailable
}
return isNetworkReachable
}
for BLE
class BLEManager: NSObject, CBCentralManagerDelegate {
var bleManagerDelegate: BLEManagerDelegate?
var bluetoothManager: CBCentralManager?
private override init() {
super.init()
bleManager = CBCentralManager(delegate: self, queue:nil,
options:[CBCentralManagerOptionShowPowerAlertKey: true])
}
//CBCentralManagerDelegate method invoked
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var bleStateError: BLEError = .kBLEStateUnknown
switch central.state {
case .poweredOff:
// powered Off
case .poweredOn:
// powered On
case .unknown:
//
case .resetting:
//
case .unsupported:
//
case .unauthorized:
//
}
}
}
//For Wifi
Use SwiftReachability https://github.com/ashleymills/Reachability.swift
//declare this property where it won't go out of scope relative to your listener
let reachability = try! Reachability()
//declare this inside of viewWillAppear
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
do{
try reachability.startNotifier()
}catch{
print("could not start reachability notifier")
}
#objc func reachabilityChanged(note: Notification) {
let reachability = note.object as! Reachability
switch reachability.connection {
case .wifi:
print("Reachable via WiFi")
case .cellular:
print("Reachable via Cellular")
case .unavailable:
print("Network not reachable")
}
}
stopping notifications
reachability.stopNotifier()
NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
// For bluetooth
import CoreBluetooth
var myBTManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
//BT Manager
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
{
if peripheral.state == .poweredOn {
// myBTManager!.startAdvertising(_broadcastBeaconDict)
} else if peripheral.state == .poweredOff {
// myBTManager!.stopAdvertising()
} else if peripheral.state == .unsupported
{
} else if peripheral.state == .unauthorized
{
}
}
I enabled to “Uses Bluetooth LE accessories” and “Acts as a Bluetooth LE accessory” in “Background Modes” settings, and these are added in Info.plist.
and also I allowed my app to access “Bluetooth sharing”.
And my app does scan BLE peripherals well on foreground mode,
but when it enter background mode, it doesn’t scan anything no more. If it enter foreground mode again, it does scan well again.
More specifically, centralManagerDidUpdateState(_ central: CBCentralManager) always does work well on any modes. but didDiscover doesn't work on background mode.
I want to BLE scanning does work well on any modes and I think I have done everything I can.
Relevant Code
BLEManager.swift
import UIKit
import CoreBluetooth
final class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralManagerDelegate {
static let shared: BLEManager = .init()
var centralManager: CBCentralManager?
var peripheralManager: CBPeripheralManager?
var scannedPeripherals: [CBPeripheral] = .init()
var confirmedPeripheralStates: [String: CBPeripheralState] = .init()
override init() {
super.init()
self.centralManager = .init(delegate: self, queue: nil)
self.peripheralManager = .init(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
self.startScan()
default:
central.stopScan()
}
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
print("[BLE] state: \(peripheral.state.rawValue)")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.scannedPeripherals.appendAsUnique(peripheral)
central.connect(peripheral)
self.confirm(peripheral)
}
func startScan() {
guard let central = self.centralManager else {
return
}
let isBackgroundMode: Bool = (UIApplication.shared.applicationState == .background)
let cbuuids: [CBUUID] = self.scannedPeripherals.map { CBUUID(nsuuid: $0.identifier) }
central.stopScan()
central.scanForPeripherals(withServices: isBackgroundMode ? cbuuids : nil)
}
func confirm(_ peripheral: CBPeripheral) {
let uuid: String = peripheral.identifier.uuidString
// Prevent duplicate logging.
if peripheral.state != self.confirmedPeripheralStates[uuid] {
self.log(peripheral)
}
self.confirmedPeripheralStates[uuid] = peripheral.state
}
func log(_ peripheral: CBPeripheral, completion: (() -> Void)? = nil) {
guard let name = peripheral.name,
let state = self.getPeripheralStateAsString(peripheral.state) else {
return
}
print("[BLE] \(name) was \(state) on \(self.getApplicationStateAsString())")
}
}
extension BLEManager {
func getApplicationStateAsString() -> String {
switch UIApplication.shared.applicationState {
case .active:
return "active"
case .inactive:
return "inactive"
case .background:
return "background"
}
}
func getPeripheralStateAsString(_ state: CBPeripheralState) -> String? {
switch state {
case .disconnected:
return "disconnected"
case .connecting:
return "connecting"
case .connected:
return "connected"
default:
return nil
}
}
}
extension Array where Element: Equatable {
mutating func appendAsUnique(_ element: Element) {
if let index = self.index(where: { $0 == element }) {
self.remove(at: index)
}
self.append(element)
}
}
AppDelegate.swift
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
BLEManager.shared.startScan()
}
Did I miss anything?
Running IOS 9.1 with Xcode 7.1.1 under 10.11.1. Cut'n'pasted this code from this tutorial; and double check it with several other sources/sites.
http://hatemfaheem.blogspot.ch/2014/12/how-would-you-scan-for-nearby-ble.html
This is the code I have ...
import Foundation
import CoreBluetooth
class BLEManager {
var centralManager : CBCentralManager!
var bleHandler : BLEHandler // delegate
init() {
self.bleHandler = BLEHandler()
self.centralManager = CBCentralManager(delegate: self.bleHandler, queue: nil)
}
}
class BLEHandler : NSObject, CBCentralManagerDelegate {
override init() {
super.init()
}
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state)
{
case .Unsupported:
print("BLE is unsupported")
case .Unauthorized:
print("BLE is unauthorized")
case .Unknown:
print("BLE is unknown")
case .Resetting:
print("BLE is reseting")
case .PoweredOff:
print("BLE is powered off")
case .PoweredOn:
print("BLE is powered on")
central.scanForPeripheralsWithServices(nil, options: nil)
default:
print("BLE default")
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("didConnectPeripheral")
}
func centralManager(central: CBCentralManager!,
didDiscoverPeripheral peripheral: CBPeripheral!,
adverismentData: [NSObject : AnyObject]!,
RSSI: NSNumber!)
{
print("\(peripheral.name) : \(RSSI) dBm")
}
}
Which I invoke in the View Controller with this code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var bleManager: BLEManager!
bleManager = BLEManager()
}
Now I run it on an iPad Air with 9.1 and plug in and unplug and replug an ibeacon but nothing appears on the console, suggesting it simply isn't working. Now I know the ibeacon is working; cause I find it with the ScanBeacon tool by Radius Networks.
OK I understand that ibeacons and Core Bluetooth don't go together so well, but surely didDiscoverPeripheral should be called ? Or have I missed a critical line in my code?
Your BLEManager is going out of scope and being deallocated at the end of viewDidLoad. Make it a member variable to give it a longer, more useful lifetime:
var bleManager = BLEManager()
override func viewDidLoad() {
// etc
}
I am using core bluetooth framework for my BLE devices and when I am scanning in iOS8 device, services found from peripherals are "180D" and "180A" but when scanned from iOS7 device, services found was "F4F2B816-A092-466E-BC76-320462D6341A".
What could be reason showing differently in different devices?
For scanning device, the following code was used:
Code
central.scanForPeripheralsWithServices(nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
Code
func centralManagerDidUpdateState(central: CBCentralManager!) {
if central.state == CBCentralManagerState.PoweredOff
{
println("bluetooth is off")
}
else if central.state == CBCentralManagerState.PoweredOn
{
IBtblScanMenu!.reloadData()
central.scanForPeripheralsWithServices(nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
println("bluetooth is on")
}
else if central.state == CBCentralManagerState.Unknown
{
println("bluetooth is unknown")
}
else if central.state == CBCentralManagerState.Unsupported
{
println("bluetooth is unsupported")
}
}
#IBAction func btnScanClicked(sender:UIButton)
{
var centralManager:CBCentralManager = CBCentralManager(delegate: self, queue: nil)
self.centralManager = centralManager
}
code
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!)
{
println(peripheral.services)
println(peripheral.services.count)
for service in peripheral.services as [CBService]
{
var alert:UIAlertView?
println("service Id is \(service.UUID)")
arrServiceID.addObject(service.UUID)
println(arrServiceID)
/* Heart Rate Service */
if service.UUID.isEqual(CBUUID.UUIDWithString("180D"))
{
peripheral.discoverCharacteristics(nil, forService: service as CBService)
}
/* Device Information Service */
if service.UUID.isEqual(CBUUID.UUIDWithString("180A"))
{
peripheral.discoverCharacteristics(nil, forService: service as CBService)
}
/* Communication with peripheral */
if service.UUID.isEqual(CBUUID.UUIDWithString("F4F2B816-A092-466E-BC76-320462D6341A"))
{
peripheral.discoverCharacteristics(nil, forService: service as CBService)
}
}
}