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
{
}
}
Related
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?
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()
}
}
I am trying to communicate through Bluetooth Low-Energy, our CBCentralManager implementation is already done in Objective-C, but I am trying to implement CBPeripheral through Swift.
I am not able to make the connection. This is how I am advertising in CBPeripheral implementation:
public func initialize()
{
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
perpheralManager = CBPeripheralManager(delegate: self, queue: queue)
startConnecting()
}
public func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager)
{
switch peripheral.state
{
case CBPeripheralManagerState.PoweredOn:
setUpServices()
break
default:break
}
}
private func setUpServices()
{
self.slideIndexCharacterestics = CBMutableCharacteristic(type: CBUUID(string: UUID.transferSlideIndex), properties: [CBCharacteristicProperties.Read,CBCharacteristicProperties.NotifyEncryptionRequired],
value: nil, permissions: CBAttributePermissions.ReadEncryptionRequired)
let services = CBMutableService(type: CBUUID(string: UUID.transferService), primary: true)
services.characteristics = [self.slideIndexCharacterestics!]
self.perpheralManager!.addService(services)
}
public func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?)
{
startConnecting()
}
func startConnecting()
{
let adData = [CBAdvertisementDataServiceUUIDsKey:CBUUID(string:"D6087B90-F460-4BE1-A9C8-E1CD8E7858E9")]
self.perpheralManager!.stopAdvertising()
self.perpheralManager!.startAdvertising(adData)
}
Note: I am able to communicate successfully if I implement in Objective-C both ends.
I'm create app only for me. Nad create it with MVC.
And i have this peace of code in my model "BTData.swift":
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state)
{
case .Unsupported:
println("BLE не поддерживается")
break
case .Unauthorized:
println("Приложение не авторизовано для использования BLE")
break
case .Unknown:
println("Состояние Central Manager не известно")
break
case .Resetting:
println("Соединение с системным сервисом потеряно")
break
case .PoweredOff:
println("BLE выключено")
break
case .PoweredOn:
startScanning()
default:
break
}
}
func startScanning() {
println("Scanning...")
if let central = centralManager {
central.scanForPeripheralsWithServices(nil, options: nil)
}
}
All works good in debug. I saw this messages there.
But i can't understand how to output State status message in the label.
I got to do with NSNotification.
Here code for model:
func sendBTServiceNotification(BTStatus: String) {
var connectionDetails = ["Статус": BTStatus]
NSNotificationCenter.defaultCenter().postNotificationName(BLEServiceStatusNotification, object: self, userInfo: connectionDetails)
}
// Получаем состояние при обновлении Central Manager (обязательная)
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state)
{
case .Unsupported:
sendBTServiceNotification("BLE не поддерживается")
break
case .Unauthorized:
sendBTServiceNotification("Приложение не авторизовано для использования BLE")
break
case .Unknown:
sendBTServiceNotification("Состояние Central Manager не известно")
break
case .Resetting:
sendBTServiceNotification("Соединение с системным сервисом потеряно")
break
case .PoweredOff:
sendBTServiceNotification("BLE выключено")
break
case .PoweredOn:
startScanning()
default:
break
}
}
func startScanning() {
sendBTServiceNotification("Сканирование...")
if let central = centralManager {
central.scanForPeripheralsWithServices(nil, options: nil)
}
}
And this code for ViewController:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
btData = BTData()
// Читаем статус BLE
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("statusChanged:"), name: btData.BLEServiceStatusNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self, name: btData.BLEServiceStatusNotification, object: nil)
}
// Обновляем статус BLE
func statusChanged(notification: NSNotification) {
let userInfo = notification.userInfo as! [String: String]
let statusMsg: String! = userInfo["Статус"]
Status.text = "Статус: \(statusMsg)"
}
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)
}
}
}