This code allows to determine current bluetooth status:
CBCentralManager* testBluetooth = [[CBCentralManager alloc] initWithDelegate:nil queue: nil];
switch ([testBluetooth state]) {....}
But, when [[CBCentralManager alloc] init...] happens, system popups an alert to user, if bluetooth is off.
Is there any way to check bluetooth status without disturbing my users?
I got the following response from an apple developer : In iOS7, the CBCentralManagerOptionShowPowerAlertKey option lets you disable this alert.
If you havea a CBCentralManager when you initialise it, you can use the method initWithDelegate:queue:options
Example:
In my .h file i have a CBCentralManager * manager
In .m file :
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], CBCentralManagerOptionShowPowerAlertKey, nil];
_manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
[_manager scanForPeripheralsWithServices:nil options:nil];
With this code the warning no longer appears, I hope that helps !
In swift you can do write these two lines in your app delegate inside the func: didFinishLaunchingWithOptions launchOptions
self.bCentralManger = CBCentralManager(delegate: self, queue: dispatch_get_main_queue(), options: [CBCentralManagerOptionShowPowerAlertKey: false])
self.bCentralManger.scanForPeripheralsWithServices(nil, options: nil)
where your bCentralManger should be declared as :
private var bCentralManger: CBCentralManager!
I have used below code to disable alert for iOS 8 and above version
self.bluetoothManager = [[CBCentralManager alloc]
initWithDelegate:self
queue:dispatch_get_main_queue()
options:#{CBCentralManagerOptionShowPowerAlertKey: #(NO)}];
[self.bluetoothManager scanForPeripheralsWithServices:nil options:nil];
By combining on BadPirate's and Anas' answer, you can get the bluetooth state without show system alert.
#import <CoreBluetooth/CoreBluetooth.h>
#interface ShopVC () <CBCentralManagerDelegate>
#property (nonatomic, strong) CBCentralManager *bluetoothManager;
#end
#implementation ShopVC
- (void)viewDidLoad {
[super viewDidLoad];
if(!self.bluetoothManager)
{
NSDictionary *options = #{CBCentralManagerOptionShowPowerAlertKey: #NO};
self.bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
}
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSString *stateString = nil;
switch(self.bluetoothManager.state)
{
case CBCentralManagerStateResetting: stateString = #"The connection with the system service was momentarily lost, update imminent."; break;
case CBCentralManagerStateUnsupported: stateString = #"The platform doesn't support Bluetooth Low Energy."; break;
case CBCentralManagerStateUnauthorized: stateString = #"The app is not authorized to use Bluetooth Low Energy."; break;
case CBCentralManagerStatePoweredOff: stateString = #"Bluetooth is currently powered off."; break;
case CBCentralManagerStatePoweredOn: stateString = #"Bluetooth is currently powered on and available to use."; break;
default: stateString = #"State unknown, update imminent."; break;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Bluetooth state"
message:stateString
delegate:nil
cancelButtonTitle:#"ok" otherButtonTitles: nil];
[alert show];
}
I've only tested this on iOS 9 so maybe someone could test this one older OS devices.
We do everything normally except one thing, instead of settings the CBCentralManager Delegate in viewDidLoad we leave this until the moment we need it, in the example case below I call this once my WKWebView has finished loading, and because each page of my web view potentially requires the use of Bluetooth I put this in WKWebView didFinishNavigation.
Swift
var managerBLE: CBCentralManager?
func bluetoothStatus() {
managerBLE = CBCentralManager(delegate: self, queue: nil, options: nil)
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
bluetoothStatus()
}
func centralManagerDidUpdateState(central: CBCentralManager) {
switch managerBLE!.state
{
case CBCentralManagerState.PoweredOff:
print("Powered Off")
case CBCentralManagerState.PoweredOn:
print("Powered On")
case CBCentralManagerState.Unsupported:
print("Unsupported")
case CBCentralManagerState.Resetting:
print("Resetting")
fallthrough
case CBCentralManagerState.Unauthorized:
print("Unauthorized")
case CBCentralManagerState.Unknown:
print("Unknown")
default:
break;
}
}
The moment that delegate is set within bluetoothStatus() you will see the state change fire.
The notification to turn on Bluetooth only seems to want to be called right at the initial load of your app, doing it this way mean you just get what you want from the centralManagerDidUpdateState
There is currently no way to disable this alert when your application is run on a iOS device which supports Bluetooth LE and where Bluetooth is disabled. It would be an enhancement request to provide a means to disable the alert. So the more requests Apple gets about this enhancement, the better.
Related
When I switch off the Bluetooth in setting, then I use CBCentralManager to get the state of the Bluetooth like this:
self.bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
the system will display a alert like this: system alert
The current state of Bluetooth is CBManagerStatePoweredOff. But when I switch off the Bluetooth in control center, this alert is not show anymore even if the current state of Bluetooth is still CBManagerStatePoweredOff.
How could I remind the user to switch on the Bluetooth in this situation?
You can remind the users by implementing the following delegate method.
//Bluetooth state delegation
#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSString *stateString = nil;
switch(self.CBManager.state)
{
case CBManagerStateResetting:
stateString = #"The connection with the system service was momentarily lost, update imminent.";
break;
case CBManagerStateUnsupported:
stateString = #"The platform doesn't support Bluetooth Low Energy."; break;
case CBManagerStateUnauthorized: stateString = #"The app is not authorized to use Bluetooth Low Energy.";
break;
case CBManagerStatePoweredOff:
stateString = #"Bluetooth is currently powered off.";
break;
case CBManagerStatePoweredOn:
[self.beaconManager startMonitoringForRegion:self.museumsRegion];
[self.beaconManager startRangingBeaconsInRegion: self.museumsRegion];
break;
case CBManagerStateUnknown:
stateString = #"State unknown, update imminent.";
break;
}
NSLog(#"%#", stateString);
}
Now the user should be informed automatically.
You can disable system BlueTooth alert by using CBCentralManagerOptionShowPowerAlertKey in options dict.
NSDictionary *options = #{CBCentralManagerOptionShowPowerAlertKey: #NO};
self.bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
Then you can using deleget method centralManagerDidUpdateState: to popup you custom alert.
I am using iBeacon Technology in my application. I'm checking in the app whether the bluetooth is enabled or disable by the user and for that i have written code below.
- (void)viewDidLoad
{
[super viewDidLoad];
_bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
}
// bluetooth manager state change
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSString *stateString = nil;
switch(central.state)
{
case CBCentralManagerStateResetting: stateString = #"The connection with the system service was momentarily lost, update imminent."; break;
case CBCentralManagerStateUnsupported: stateString = #"The platform doesn't support Bluetooth Low Energy."; break;
case CBCentralManagerStateUnauthorized: stateString = #"The app is not authorized to use Bluetooth Low Energy."; break;
case CBCentralManagerStatePoweredOff: stateString = #"Bluetooth is currently powered off."; break;
case CBCentralManagerStatePoweredOn: stateString = #"Bluetooth is currently powered on and available to use."; break;
default: stateString = #"State unknown, update imminent."; break;
}
}
Problem : This above alert Message prompts very oftenly in background or when the other app is open even if i have kill the application. I want to show this default alert message but only when the user opens the app.
Can anyone provide me a solution?
self.bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:#{CBCentralManagerOptionShowPowerAlertKey: #NO}];
This maybe works to hide the system pop up.
Okay, I'm working on a fun project that has a hurdle where I need to enable Bluetooth audio support for my iOS app.
The hurdle I'm at is that I simply can't even begin to get a list of connected Bluetooth audio devices. Even though my iPhone 5S recognizes my earpiece (a ~3 - 4 year old LG HBM-230, to be precise) and plays audio through it for phone calls, BOTH External Accessory and CoreBluetooth are giving me nothing useful when I query both.
I'm basing my own code off questions & answers I found for both the CoreBluetooth and External Accessory frameworks.
When my code simply tries to "scanForPeripheralsWithServices:nil" for any Bluetooth devices which Settings->Bluetooth say are visible and connected, the below code simply is NOT coming up with a single hit beyond the "CBCentralManagerStatePoweredOn" message in the console.
And this line in my code (with a valid EAAccessoryManager instance)
NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];
also comes back with a nil array.
What could I be doing wrong?
B.T.W., I've made this code available as a GitHub project.
#implementation BluetoothManager
+ (BluetoothManager *)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _bluetoothMGR = nil;
dispatch_once(&pred, ^{
_bluetoothMGR = [[BluetoothManager alloc] init];
});
return _bluetoothMGR;
}
- (id)init
{
self = [super init];
if(self)
{
dispatch_queue_t centralQueue = dispatch_queue_create("com.yo.mycentral", DISPATCH_QUEUE_SERIAL);
// whether we try this on a queue of "nil" (the main queue) or this separate thread, still not getting results
self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:nil];
}
return self;
}
// this would hit.... if I instantiated this in a storyboard of XIB file
- (void)awakeFromNib
{
if(!self.cbManager)
self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"hey I found %#",[advertisementData description]);
}
- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
NSLog( #"I retrieved CONNECTED peripherals");
}
-(void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals{
NSLog(#"This is it!");
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSString *messtoshow;
switch (central.state) {
case CBCentralManagerStateUnknown:
{
messtoshow=#"State unknown, update imminent.";
break;
}
case CBCentralManagerStateResetting:
{
messtoshow=#"The connection with the system service was momentarily lost, update imminent.";
break;
}
case CBCentralManagerStateUnsupported:
{
messtoshow=#"The platform doesn't support Bluetooth Low Energy";
break;
}
case CBCentralManagerStateUnauthorized:
{
messtoshow=#"The app is not authorized to use Bluetooth Low Energy";
break;
}
case CBCentralManagerStatePoweredOff:
{
messtoshow=#"Bluetooth is currently powered off.";
break;
}
case CBCentralManagerStatePoweredOn:
{
messtoshow=#"Bluetooth is currently powered on and available to use.";
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[_cbManager scanForPeripheralsWithServices:nil options:options];
break;
}
}
NSLog(#"%#", messtoshow);
}
#end
First you will need to configure your applications audio session to allow bluetooth connections that support audio. You can do this in, for example, your application delegates - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method. Make sure you link the AVFoundation Framework and import in headers that will use it.
#import <AVFoundation/AVFoundation.h>// place in .h
[self prepareAudioSession];// called from application didFinishLaunchingWithOptions
- (BOOL)prepareAudioSession {
// deactivate session
BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
if (!success) {
NSLog(#"deactivationError");
}
// set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
if (!success) {
NSLog(#"setCategoryError");
}
// activate audio session
success = [[AVAudioSession sharedInstance] setActive:YES error: nil];
if (!success) {
NSLog(#"activationError");
}
return success;
}
Every application has an audio session singleton that you can configure. The sessions category and mode (in this example I did not set the mode so it reverts to the default mode) declare your applications intentions as to how you would like audio routing to be handled. It follows an important rule of last in wins. This means that if the user plugs in a headset or in this case a bluetooth device that is a hands free peripheral (HFP) the system will automatically route the audio to the headset or bluetooth device. The users physical actions are used to determine audio routing. However if you wish to give the user a list of available routes Apple recommend using MPVolumeView class.
An example for adding MPVolumeView could be put in a UIViewController subclasses viewDidLoad method.
#import <MediaPlayer/MediaPlayer.h> // place in .h
// prefered way using MPVolumeView for user selecting audio routes
self.view.backgroundColor = [UIColor clearColor];
CGRect frameForMPVV = CGRectMake(50.0, 50.0, 100.0, 100.0);
MPVolumeView *routeView = [[MPVolumeView alloc] initWithFrame:frameForMPVV];
[routeView setShowsVolumeSlider:NO];
[routeView setShowsRouteButton:YES];
[self.view addSubview: routeView];
As of iOS 7 you can get all inputs like this
// portDesc.portType could be for example - BluetoothHFP, MicrophoneBuiltIn, MicrophoneWired
NSArray *availInputs = [[AVAudioSession sharedInstance] availableInputs];
int count = [availInputs count];
for (int k = 0; k < count; k++) {
AVAudioSessionPortDescription *portDesc = [availInputs objectAtIndex:k];
NSLog(#"input%i port type %#", k+1, portDesc.portType);
NSLog(#"input%i port name %#", k+1, portDesc.portName);
}
The portType you would be interested in is "BluetoothHFP". The portName property typically is the manufacturer/model which is what you would show to the user. (I've checked this with a non-LE bluetooth Motorola dinosaur and it works)
Because of the last in wins rule you will need to observe these two notifications (iOS 7 included). One to handle interruptions (such as phone calls or an alarm) and the second to be notified of route changes. Route change notifications is the one related to this question.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myInterruptionSelector:)
name:AVAudioSessionInterruptionNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myRouteChangeSelector:)
name:AVAudioSessionRouteChangeNotification
object:nil];
For iOS 6.x you could read the currentRoute property of AVAudioSession inside the myRouteChange: selector to get the new route, as this will get called when a headset or bluetooth device is connected.
- (void)myRouteChangeSelector:(NSNotification*)notification {
AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
NSArray *inputsForRoute = currentRoute.inputs;
NSArray *outputsForRoute = currentRoute.outputs;
AVAudioSessionPortDescription *outPortDesc = [outputsForRoute objectAtIndex:0];
NSLog(#"current outport type %#", outPortDesc.portType);
AVAudioSessionPortDescription *inPortDesc = [inputsForRoute objectAtIndex:0];
NSLog(#"current inPort type %#", inPortDesc.portType);
}
Any iOS version < 6.0 you'll need the 'now deprecated' AudioSessionServices class. This class is a C api that instead of notifications it allows you to add property listeners.
I'll finish on this note - YOU DONT ALWAYS GET WHAT YOU WANT from the system. There are interruption handling notifications to observe and lots of error checking needed. I think this is a really good question and I hope this sheds some light on what it is you are trying to achieve.
Swift version
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .allowBluetooth)
try AVAudioSession.sharedInstance().setActive(true)
} catch {}
let availableInputs = AVAudioSession.sharedInstance().availableInputs
Okay, I'm working on a fun project that has a hurdle where I need to enable Bluetooth audio support for my iOS app.
The hurdle I'm at is that I simply can't even begin to get a list of connected Bluetooth audio devices. Even though my iPhone 5S recognizes my earpiece (a ~3 - 4 year old LG HBM-230, to be precise) and plays audio through it for phone calls, BOTH External Accessory and CoreBluetooth are giving me nothing useful when I query both.
I'm basing my own code off questions & answers I found for both the CoreBluetooth and External Accessory frameworks.
When my code simply tries to "scanForPeripheralsWithServices:nil" for any Bluetooth devices which Settings->Bluetooth say are visible and connected, the below code simply is NOT coming up with a single hit beyond the "CBCentralManagerStatePoweredOn" message in the console.
And this line in my code (with a valid EAAccessoryManager instance)
NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];
also comes back with a nil array.
What could I be doing wrong?
B.T.W., I've made this code available as a GitHub project.
#implementation BluetoothManager
+ (BluetoothManager *)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _bluetoothMGR = nil;
dispatch_once(&pred, ^{
_bluetoothMGR = [[BluetoothManager alloc] init];
});
return _bluetoothMGR;
}
- (id)init
{
self = [super init];
if(self)
{
dispatch_queue_t centralQueue = dispatch_queue_create("com.yo.mycentral", DISPATCH_QUEUE_SERIAL);
// whether we try this on a queue of "nil" (the main queue) or this separate thread, still not getting results
self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:nil];
}
return self;
}
// this would hit.... if I instantiated this in a storyboard of XIB file
- (void)awakeFromNib
{
if(!self.cbManager)
self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"hey I found %#",[advertisementData description]);
}
- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
NSLog( #"I retrieved CONNECTED peripherals");
}
-(void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals{
NSLog(#"This is it!");
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSString *messtoshow;
switch (central.state) {
case CBCentralManagerStateUnknown:
{
messtoshow=#"State unknown, update imminent.";
break;
}
case CBCentralManagerStateResetting:
{
messtoshow=#"The connection with the system service was momentarily lost, update imminent.";
break;
}
case CBCentralManagerStateUnsupported:
{
messtoshow=#"The platform doesn't support Bluetooth Low Energy";
break;
}
case CBCentralManagerStateUnauthorized:
{
messtoshow=#"The app is not authorized to use Bluetooth Low Energy";
break;
}
case CBCentralManagerStatePoweredOff:
{
messtoshow=#"Bluetooth is currently powered off.";
break;
}
case CBCentralManagerStatePoweredOn:
{
messtoshow=#"Bluetooth is currently powered on and available to use.";
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[_cbManager scanForPeripheralsWithServices:nil options:options];
break;
}
}
NSLog(#"%#", messtoshow);
}
#end
First you will need to configure your applications audio session to allow bluetooth connections that support audio. You can do this in, for example, your application delegates - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method. Make sure you link the AVFoundation Framework and import in headers that will use it.
#import <AVFoundation/AVFoundation.h>// place in .h
[self prepareAudioSession];// called from application didFinishLaunchingWithOptions
- (BOOL)prepareAudioSession {
// deactivate session
BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
if (!success) {
NSLog(#"deactivationError");
}
// set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
if (!success) {
NSLog(#"setCategoryError");
}
// activate audio session
success = [[AVAudioSession sharedInstance] setActive:YES error: nil];
if (!success) {
NSLog(#"activationError");
}
return success;
}
Every application has an audio session singleton that you can configure. The sessions category and mode (in this example I did not set the mode so it reverts to the default mode) declare your applications intentions as to how you would like audio routing to be handled. It follows an important rule of last in wins. This means that if the user plugs in a headset or in this case a bluetooth device that is a hands free peripheral (HFP) the system will automatically route the audio to the headset or bluetooth device. The users physical actions are used to determine audio routing. However if you wish to give the user a list of available routes Apple recommend using MPVolumeView class.
An example for adding MPVolumeView could be put in a UIViewController subclasses viewDidLoad method.
#import <MediaPlayer/MediaPlayer.h> // place in .h
// prefered way using MPVolumeView for user selecting audio routes
self.view.backgroundColor = [UIColor clearColor];
CGRect frameForMPVV = CGRectMake(50.0, 50.0, 100.0, 100.0);
MPVolumeView *routeView = [[MPVolumeView alloc] initWithFrame:frameForMPVV];
[routeView setShowsVolumeSlider:NO];
[routeView setShowsRouteButton:YES];
[self.view addSubview: routeView];
As of iOS 7 you can get all inputs like this
// portDesc.portType could be for example - BluetoothHFP, MicrophoneBuiltIn, MicrophoneWired
NSArray *availInputs = [[AVAudioSession sharedInstance] availableInputs];
int count = [availInputs count];
for (int k = 0; k < count; k++) {
AVAudioSessionPortDescription *portDesc = [availInputs objectAtIndex:k];
NSLog(#"input%i port type %#", k+1, portDesc.portType);
NSLog(#"input%i port name %#", k+1, portDesc.portName);
}
The portType you would be interested in is "BluetoothHFP". The portName property typically is the manufacturer/model which is what you would show to the user. (I've checked this with a non-LE bluetooth Motorola dinosaur and it works)
Because of the last in wins rule you will need to observe these two notifications (iOS 7 included). One to handle interruptions (such as phone calls or an alarm) and the second to be notified of route changes. Route change notifications is the one related to this question.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myInterruptionSelector:)
name:AVAudioSessionInterruptionNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myRouteChangeSelector:)
name:AVAudioSessionRouteChangeNotification
object:nil];
For iOS 6.x you could read the currentRoute property of AVAudioSession inside the myRouteChange: selector to get the new route, as this will get called when a headset or bluetooth device is connected.
- (void)myRouteChangeSelector:(NSNotification*)notification {
AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
NSArray *inputsForRoute = currentRoute.inputs;
NSArray *outputsForRoute = currentRoute.outputs;
AVAudioSessionPortDescription *outPortDesc = [outputsForRoute objectAtIndex:0];
NSLog(#"current outport type %#", outPortDesc.portType);
AVAudioSessionPortDescription *inPortDesc = [inputsForRoute objectAtIndex:0];
NSLog(#"current inPort type %#", inPortDesc.portType);
}
Any iOS version < 6.0 you'll need the 'now deprecated' AudioSessionServices class. This class is a C api that instead of notifications it allows you to add property listeners.
I'll finish on this note - YOU DONT ALWAYS GET WHAT YOU WANT from the system. There are interruption handling notifications to observe and lots of error checking needed. I think this is a really good question and I hope this sheds some light on what it is you are trying to achieve.
Swift version
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .allowBluetooth)
try AVAudioSession.sharedInstance().setActive(true)
} catch {}
let availableInputs = AVAudioSession.sharedInstance().availableInputs
I trying to get the Status of iPhone/iPod Bluetooth that whether it is ON or OFF programmatically.
Is it possible using some Apple API or third party API.
A little bit of research into Sam's answer that I thought I'd share
You can do so without utilizing private API, but with a few caveats:
It will only work on iOS 5.0+
It will only work on devices that
support the bluetooth LE spec (iPhone 4S+, 5th Generation iPod+, iPad
3rd Generation+)
Simply allocating the class will cause your application to ask permission to use the bluetooth stack from the user (may not be desired), and if they refuse, the only thing you'll see is CBCentralManagerStateUnauthorized iOS7+ Revision: Aforementioned strike-through can now be prevented, see comments below which point to this answer which explains you can set CoreBluetooth's CBCentralManagerOptionShowPowerAlertKey option to NO to prevent permissions prompt.
Retrieval of bluetooth state is async, and continuous. You will need to setup a delegate to get state changes, as checking the state of a freshly allocated bluetooth manager will return CBCentralManagerStateUnknown
That being said, this method does seem to provide real time updates of bluetooth stack state.
After including the CoreBluetooth framework,
#import <CoreBluetooth/CoreBluetooth.h>
These tests were easy to perform using:
- (void)detectBluetooth
{
if(!self.bluetoothManager)
{
// Put on main queue so we can call UIAlertView from delegate callbacks.
self.bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
}
[self centralManagerDidUpdateState:self.bluetoothManager]; // Show initial state
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSString *stateString = nil;
switch(self.bluetoothManager.state)
{
case CBCentralManagerStateResetting: stateString = #"The connection with the system service was momentarily lost, update imminent."; break;
case CBCentralManagerStateUnsupported: stateString = #"The platform doesn't support Bluetooth Low Energy."; break;
case CBCentralManagerStateUnauthorized: stateString = #"The app is not authorized to use Bluetooth Low Energy."; break;
case CBCentralManagerStatePoweredOff: stateString = #"Bluetooth is currently powered off."; break;
case CBCentralManagerStatePoweredOn: stateString = #"Bluetooth is currently powered on and available to use."; break;
default: stateString = #"State unknown, update imminent."; break;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Bluetooth state"
message:stateString
delegate:nil
cancelButtonTitle:#"ok" otherButtonTitles: nil];
[alert show];
}
To disable the default alert message you just need to pass through an option dictionary when you instantiate the CBPeripheralManager:
SWIFT tested on iOS8+
import CoreBluetooth
//Define class variable in your VC/AppDelegate
var bluetoothPeripheralManager: CBPeripheralManager?
//On viewDidLoad/didFinishLaunchingWithOptions
let options = [CBCentralManagerOptionShowPowerAlertKey:0] //<-this is the magic bit!
bluetoothPeripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: options)
Obviously you also need to implement the CKManagerDelegate delegate method peripheralManagerDidUpdateState as outlined above as well:
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) {
var statusMessage = ""
switch peripheral.state {
case .poweredOn:
statusMessage = "Bluetooth Status: Turned On"
case .poweredOff:
statusMessage = "Bluetooth Status: Turned Off"
case .resetting:
statusMessage = "Bluetooth Status: Resetting"
case .unauthorized:
statusMessage = "Bluetooth Status: Not Authorized"
case .unsupported:
statusMessage = "Bluetooth Status: Not Supported"
case .unknown:
statusMessage = "Bluetooth Status: Unknown"
}
print(statusMessage)
if peripheral.state == .poweredOff {
//TODO: Update this property in an App Manager class
}
}
This answer has been updated from the original Objective-C to Swift 4.0.
It is assumed that you have already created a bluetooth manager and assigned the delegate to the ViewController class.
import CoreBluetooth
extension ViewController : CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("powered on")
case .poweredOff:
print("powered off")
case .resetting:
print("resetting")
case .unauthorized:
print("unauthorized")
case .unsupported:
print("unsupported")
case .unknown:
print("unknown")
}
}
}
Some updates on BadPirate's answer, with iOS7 you can set the central manager not to show the alert when allocating the manager object by giving it a NSDictionary that has key "CBCentralManagerOptionShowPowerAlertKey" set to 0.
self.cbManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:
[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:0]
forKey:CBCentralManagerOptionShowPowerAlertKey]];
There is a way on iOS 5 and above using CoreBluetooth. The class you can use is CBCentralManager. It has a property 'state' that you can check to see if Bluetooth is on or not. (the enum CBCentralManagerState has the value(s) you want to check against).
Once you have the CBCentralManager setup you can use CBCentralManager::state and CBCentralManager::authorization either from a delegate method or directly.
import CoreBluetooth
class Manager {
let centralManager = CBCentralManager(delegate: self, queue: nil)
var isBTTurnedOn: Bool {
return centralManager.state == .poweredOn
}
var isAuthorized: Bool {
if #available(iOS 13.0, *) {
return centralManager.authorization == .allowedAlways
} else {
return true
}
}
}
This solution is bit old , before apple introducing core bluetooth
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
Class BluetoothManager = objc_getClass( "BluetoothManager" ) ;
id btCont = [BluetoothManager sharedInstance] ;
[self performSelector:#selector(status:) withObject:btCont afterDelay:1.0f] ;
return YES ;
}
- (void)status:(id)btCont
{
BOOL currentState = [btCont enabled] ;
//check the value of currentState
}