7

In my project, I use AVAudioSession to detect any headphone is plugged or unplugged. But in this case, I can't detect when bluetooth device is plugged. Here is my code for headphone state.

 - (void)audioRouteChangeListenerCallback:(NSNotification*)notification
    {

    NSDictionary *interuptionDict = notification.userInfo;

    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];

    switch (routeChangeReason) {

        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");

            NSLog(@"Headphone/Line plugged in");

            [_soundButtonOutlet setImage:[UIImage imageNamed:@"sound-on.png"] forState:UIControlStateNormal];

            _headSetState=YES;

            break;

        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");

            NSLog(@"Headphone/Line was pulled. Stopping player....");

             [_soundButtonOutlet setImage:[UIImage imageNamed:@"sound-off.png"] forState:UIControlStateNormal];
            if(_isPlaying==YES)
            {


            [self.player pause];

            [_audioButtonOutlet setImage:[UIImage imageNamed:@"play.png"] forState:UIControlStateNormal];

            _isPlaying=NO;

            }
            _headSetState=NO;

            break;

        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");


            break;
    }



- (BOOL)isHeadsetPluggedIn

{

    AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
    for (AVAudioSessionPortDescription* desc in [route outputs]) {

        if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
        {
        [_soundButtonOutlet setImage:[UIImage imageNamed:@"sound-on.png"] forState:UIControlStateNormal];
            _headSetState=YES;
            return YES;
        }
        else
        {
    [_soundButtonOutlet setImage:[UIImage imageNamed:@"sound-off.png"] forState:UIControlStateNormal];
            _headSetState=NO;
            return NO;

        }
    }


    return NO;
}

}


- viewWillAppear  {

 [AVAudioSession sharedInstance];

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

[self isHeadsetPluggedIn];

}

So how can I detect if a bluetooth headset plugged or not iOS 8?

Alex Terente
  • 11,770
  • 5
  • 47
  • 66

3 Answers3

19

You can detect currently active bluetooth output devices (instead of input devices)

Swift Code:

import AVFoundation
func bluetoothAudioConnected() -> Bool{
  let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
  for output in outputs{
    if output.portType == AVAudioSessionPortBluetoothA2DP || output.portType == AVAudioSessionPortBluetoothHFP || output.portType == AVAudioSessionPortBluetoothLE{
      return true
    }
  }
  return false
}

Bluetooth devices are based on the following question: What's the difference among AVAudioSessionPortBluetoothHFP, A2DP and LE?

I hope it helps someone


Edit for Swift 5.1 (Thanks iago849 for the fix)

var bluetoothDeviceConnected: Bool {
    !AVAudioSession.sharedInstance().currentRoute.outputs.compactMap {
        ($0.portType == .bluetoothA2DP ||
        $0.portType == .bluetoothHFP ||
        $0.portType == .bluetoothLE) ? true : nil
    }.isEmpty
}
Gerrit Post
  • 1,129
  • 12
  • 25
  • This won't tell you if you're dealing with a headset; just some arbitrary Bluetooth audio device. – mrgrieves Aug 11 '17 at 23:15
  • for (swift 5.2) to check if a wired headset is connected or not just use : if output.portType == AVAudioSession.Port.headphones { } – Debashish Das Jul 20 '20 at 07:14
  • cool, my friend, much better approach for those when enabling input is unacceptable (audio quality in airpods suffer so much for example). I wish I could give you 10 upvotes – iago849 May 03 '21 at 11:19
  • your 5.1 solution is obviously WRONG because it will always return true - even if array contains falses – iago849 May 04 '21 at 07:45
  • 1
    thank you for the fix! – iago849 May 05 '21 at 10:58
3

I was able to detect whether a bluetooth headset (HFP) device was currently connected using the following:

NSArray *arrayInputs = [[AVAudioSession sharedInstance] availableInputs];
for (AVAudioSessionPortDescription *port in arrayInputs)
{
    if ([port.portType isEqualToString:AVAudioSessionPortBluetoothHFP])
    {
        bHas = YES;
        break;
    }
}

However, your AVAudioSession category must be set as AVAudioSessionCategoryPlayAndRecord in order for this to work. If it isn't, the port will not show up in the list even if the HFP device is connected.

Adam
  • 46
  • 1
0

You can detect it with routeChangeNotification:

    func activateHeadPhonesStatus(){
        NotificationCenter.default.addObserver(self, selector: #selector(audioRouteChangeListener(_:)), name: AVAudioSession.routeChangeNotification, object: nil)
    }
    
    @objc func audioRouteChangeListener(_ notification:Notification) {
        guard let userInfo = notification.userInfo,
              let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
              let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
            return
        }
        if reason == .newDeviceAvailable {
            let session = AVAudioSession.sharedInstance()
            for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.bluetoothA2DP {
                print("Bluetooth Headphone Connected")
                break
            }
        }
    }
Sasan Log
  • 409
  • 4
  • 13
  • @famfamfam could you be more specific please? – Sasan Log Mar 17 '21 at 10:30
  • sorry bro, after 1 days without sleep, im commented wrong tab, but i wish u can help me here : https://stackoverflow.com/questions/66631995/swift-custom-context-menu-previewprovider-can-not-click-any-view-insideusing-ta, thanks in advance – famfamfam Mar 17 '21 at 11:38