Swift|Headphone Unplug Detectionと自動停止のしくみ|イヤホン抜け検知
作成日:2025-11-09
更新日:2025-11-09

急な音漏れを
防ごう
防ごう

イヤホンをはずしたことが、どうしてわかるの?

iPhoneは、イヤホンが抜けた瞬間をOSが検知してアプリに知らせてくれる。
その合図を受けて止める仕組みが、今回のテーマ。
iOSが「イヤホン抜けた!」をどうやって知らせるか
iPhoneでは、イヤホンが抜けたりBluetooth接続が切れたりすると、
iOSが自動でオーディオ経路の変化を検知してアプリに通知を送る。
この仕組みを使えば
自分のアプリで「スピーカーから急に音が鳴る事故」を防げる
イヤホン抜けの通知を受け取るコード
import AVFoundation
final class AudioSessionManager {
private let nc = NotificationCenter.default
var onHeadphoneUnplugged: (() -> Void)?
func activate() throws {
let s = AVAudioSession.sharedInstance()
try s.setCategory(.playback, options: [.mixWithOthers])
try s.setActive(true)
observeRouteChange()
}
private func observeRouteChange() {
nc.addObserver(forName: AVAudioSession.routeChangeNotification,
object: nil, queue: .main) { [weak self] note in
guard let self,
let info = note.userInfo,
let reasonRaw = info[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonRaw)
else { return }
// 旧ルート(抜けたデバイスを知る)
let prev = info[AVAudioSessionRouteChangePreviousRouteKey]
as? AVAudioSessionRouteDescription
if reason == .oldDeviceUnavailable,
let outputs = prev?.outputs {
let wasHeadphones = outputs.contains { out in
out.portType == .headphones ||
out.portType == .bluetoothA2DP ||
out.portType == .bluetoothHFP ||
out.portType == .bluetoothLE
}
if wasHeadphones {
onHeadphoneUnplugged?() // ここで停止/フェードダウンなどを行う
}
}
}
}
}
呼び出し側の処理(フェード停止の例)
// LocalAudioEngine.swift 側
engineSession.onHeadphoneUnplugged = { [weak self] in
self?.fade(to: 0.0, duration: 0.4) { [weak self] in
self?.stop()
// 必要ならUIへ「イヤホンが外れたため停止しました」を通知
}
}
このコードは絶対に必要
このコード(AVAudioSession.routeChangeNotification の購読)を書かない限り、アプリは何も気づかない
iOSが「イヤホン抜けた〜!」って通知を投げてはくれるけど、アプリが受け取る“仕組み”を組み込んでおかなければ無意味になる。
| レイヤー | 役割 | やってくれる? |
|---|---|---|
| iOS(システム) | イヤホン抜けを検知して通知を発行する | 自動でやってくれる |
| アプリ | 通知を「購読」して中身を見て、止める処理を書く | 自分で書かないと動かない |

受け取らなければ無意味
だから注意点
NotificationCenterの購読登録(addObserver) がないと、通知をキャッチできないsetActive(true)でAVAudioSessionを有効化してないと、そもそも通知が来ないこともある- フェード処理や停止処理 もアプリ側の実装に任されてる(自動停止はしてくれない)
一言で言うと
iOSは「知らせるだけ」。
止める・フェードするのはアプリの責任。

もういちど、流れを整理
仕組みの流れ
- 物理的な変化が起こる
イヤホンが抜ける/Bluetoothが切れる。 - Core Audioが経路変更を検知
出力ルートから「ヘッドフォン」が消える。 - AVAudioSessionが通知を発行
AVAudioSession.routeChangeNotificationがNotificationCenter経由で送られる。 - アプリが通知を受け取る
userInfoから「理由(Reason)」と「直前のルート(PreviousRoute)」を確認し、
「イヤホンが使われていたなら停止やフェードを実行」。
つまり、アプリが“定期的にチェックする必要はない”。
OSが変化を検知してイベントを投げてくれる仕組み。
理由コード(抜粋)
| Reason | 意味 |
|---|---|
.oldDeviceUnavailable | 前の出力が消えた(イヤホンが抜けた/BT切断) |
.newDeviceAvailable | 新しい出力が追加された(BT接続など) |
.categoryChange | セッションカテゴリ変更 |
.routeConfigurationChange | 経路構成の変更(複合的) |
実装のコツ
queue: .mainでUI操作やフェードを安全に処理- 多重発火を防ぐフラグを持つ(連続通知が来る機種がある)
- AirPlayは
portType == .airPlayなので別扱い - 実運用では
AVAudioSession.sharedInstance().setActive(true)を最初に呼ぶと安定
1行まとめ
イヤホンが抜けたら、OSが自動で「経路変わったで」と知らせる。アプリはそれを受けて止めるだけ。
Vocabulary
| English | Japanese |
|---|---|
| route change notification | 経路変化通知 |
| NotificationCenter | 通知センター |
| userInfo | 通知情報ディクショナリ |
| previous route | 直前のオーディオ経路 |
| oldDeviceUnavailable | 旧デバイス消失 |
| AirPlay | エアプレイ |
| observer | オブザーバ(購読者) |
Silence is golden—especially when your app knows when to stay quiet.
2025-11-09
編集後記:
この記事の内容がベストではないかもしれません。