Cómo encontrar dispositivos de audio Bluetooth en iOS

Está bien, estoy trabajando en un proyecto divertido que tiene un obstáculo donde necesito habilitar el soporte de audio Bluetooth para mi aplicación iOS.

El obstáculo en el que estoy es que simplemente no puedo ni siquiera comenzar a get una list de dispositivos de audio Bluetooth conectados. A pesar de que mi iPhone 5S reconoce mi auricular (un LG HBM-230 de ~3 a 4 años de edad, para ser precisos) y reproduce audio a través de él para llamadas telefónicas, AMBOS accesorios externos y CoreBluetooth no me dan nada útil cuando busco ambos.

Estoy basando mi propio código en preguntas y respuestas que encontré para los frameworks CoreBluetooth y External Accessory .

Cuando mi código simplemente trata de " scanForPeripheralsWithServices:nil " para cualquier dispositivo Bluetooth que Configuración-> Bluetooth dice que son visibles y conectados, el código siguiente simplemente NO viene con un solo golpe más allá del post " CBCentralManagerStatePowenetworkingOn " en la console.

Y esta línea en mi código (con una instancia EAAccessoryManager válida)

 NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories]; 

También regresa con una matriz nula.

¿Qué podría estar haciendo mal?

Por cierto, he hecho que este código esté disponible como un proyecto GitHub .

 @implementation BluetoothManager + (BluetoothManager *)shanetworkingInstance { static dispatch_once_t pnetworking = 0; __strong static id _bluetoothMGR = nil; dispatch_once(&pnetworking, ^{ _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 CBCentralManagerStatePowenetworkingOff: { messtoshow=@"Bluetooth is currently powenetworking off."; break; } case CBCentralManagerStatePowenetworkingOn: { messtoshow=@"Bluetooth is currently powenetworking on and available to use."; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil]; [_cbManager scanForPeripheralsWithServices:nil options:options]; break; } } NSLog(@"%@", messtoshow); } @end 

Primero necesitará configurar la session de audio de sus aplicaciones para permitir conexiones bluetooth que admitan audio. Puede hacer esto en, por ejemplo, su aplicación de delegates de aplicación (BOOL): aplicación (UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) método launchOptions. Asegúrese de vincular el AVFoundation Framework e importar los encabezados que lo usarán.

 #import <AVFoundation/AVFoundation.h>// place in .h [self prepareAudioSession];// called from application didFinishLaunchingWithOptions - (BOOL)prepareAudioSession { // deactivate session BOOL success = [[AVAudioSession shanetworkingInstance] setActive:NO error: nil]; if (!success) { NSLog(@"deactivationError"); } // set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth success = [[AVAudioSession shanetworkingInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil]; if (!success) { NSLog(@"setCategoryError"); } // activate audio session success = [[AVAudioSession shanetworkingInstance] setActive:YES error: nil]; if (!success) { NSLog(@"activationError"); } return success; } 

Cada aplicación tiene un singleton de session de audio que puede configurar. La categoría y el modo de las sesiones (en este ejemplo, no configuré el modo para que vuelva al modo pnetworkingeterminado) declara las intenciones de tus aplicaciones en cuanto a cómo te gustaría manejar el routing de audio. Sigue una regla importante de la última en victorias . Esto significa que si el usuario se conecta a un auricular o en este caso a un dispositivo bluetooth que es un periférico manos libres (HFP), el sistema enrutará automáticamente el audio al auricular o al dispositivo bluetooth. Las acciones físicas de los usuarios se utilizan para determinar el routing de audio. Sin embargo, si desea proporcionar al usuario una list de routes disponibles, Apple recomienda utilizar la class MPVolumeView.

Un ejemplo para agregar MPVolumeView podría colocarse en un método viewDidLoad de subclasss UIViewController.

 #import <MediaPlayer/MediaPlayer.h> // place in .h // prefenetworking 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]; 

A partir de iOS 7 puede get todas las inputs como esta

 // portDesc.portType could be for example - BluetoothHFP, MicrophoneBuiltIn, MicrophoneWinetworking NSArray *availInputs = [[AVAudioSession shanetworkingInstance] 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); } 

El tipo de puerto en el que estaría interesado es "BluetoothHFP". La propiedad portName generalmente es el fabricante / model que es lo que mostrarías al usuario. (He comprobado esto con un dinosaurio Motorola sin bluetooth LE y funciona)

Debido a la última regla de victorias , necesitarás observar estas dos notifications (iOS 7 incluido). Uno para manejar las interrupciones (como llamadas telefónicas o una alarma) y el segundo para ser notificado de los cambios de ruta. Las notifications de cambio de ruta son las relacionadas con esta pregunta.

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myInterruptionSelector:) name:AVAudioSessionInterruptionNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myRouteChangeSelector:) name:AVAudioSessionRouteChangeNotification object:nil]; 

Para iOS 6.x, puede leer la propiedad currentRoute de AVAudioSession dentro del selector myRouteChange: para get la nueva ruta, ya que se llamará cuando esté conectado un auricular o un dispositivo bluetooth.

 - (void)myRouteChangeSelector:(NSNotification*)notification { AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession shanetworkingInstance] 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); } 

Cualquier versión de iOS <6.0 necesitará la class 'AudioSessionServices' actualmente en desuso . Esta class es una C api que, en lugar de notifications, le permite agregar oyentes de propiedad.

Terminaré con esta nota: NO OBTENER SIEMPRE LO QUE DESEE del sistema. Hay notifications de manipulación de interrupciones para observar y se necesita mucha verificación de errores. Creo que esta es una muy buena pregunta y espero que esto arroje algo de luz sobre lo que está tratando de lograr.