◉RORK LABEN
●EXPO — EAS Observe がネイティブクラッシュも記録(10/07)。SDK 57 は 57.0.21 以降で確認できます●SDK 58 — SDK 58 Beta は 9/15 公開。stable の日付はまだ未確認です●RN 0.88 — React Native 0.88.x の正式リリース予定は 10/12、残り3日●Q&A — expo-widgets が本番ビルドだけ真っ黒になる、という問いが出ています●RORK — 09-29 に GPT-6.1 Sol を追加。Pro・Max プランで利用できます●NEW — クライアントのアプリを作る前に決める、公開アカウントの持ち主●EXPO — EAS Observe がネイティブクラッシュも記録(10/07)。SDK 57 は 57.0.21 以降で確認できます●SDK 58 — SDK 58 Beta は 9/15 公開。stable の日付はまだ未確認です●RN 0.88 — React Native 0.88.x の正式リリース予定は 10/12、残り3日●Q&A — expo-widgets が本番ビルドだけ真っ黒になる、という問いが出ています●RORK — 09-29 に GPT-6.1 Sol を追加。Pro・Max プランで利用できます●NEW — クライアントのアプリを作る前に決める、公開アカウントの持ち主
記事一覧/開発ツール
⬡ 開発ツール/2026-06-24上級

電話・イヤホン抜き・他アプリ再生に強い、音声アプリの中断ハンドリング

AVAudioSessionの中断通知とルート変更通知を正しく扱い、電話の着信やイヤホンの抜き差しで再生が破綻しない音声アプリを作ります。オーディオカテゴリの決め方、Now Playingとリモートコマンドの接続、バックグラウンド再生のテストまでSwiftコードで整理します。

Rork Max235AVFoundationオーディオSwiftUI66バックグラウンド再生4

✦ プレミアム記事

瞑想や学習向けの音声を再生する小さなアプリを個人開発で出したとき、最初に届いた不具合報告が「電話が来た後、音が戻ってこない」というものでした。再生中に着信があると音が止まるのは当然なのですが、通話が終わってもアプリは止まったまま、画面の再生ボタンは「再生中」の見た目のまま固まっていたのです。

原因は、AVAudioSession の中断を一切ハンドリングしていなかったことでした。再生は AVAudioPlayer に任せきりで、OS から「いま中断したよ」「もう戻っていいよ」という通知が来ていることに、私のコードがまったく耳を傾けていなかったのです。音声アプリの品質は、機能が一通り動くかどうかよりも、この種の割り込みにどれだけ丁寧に応えるかで決まると、その時に痛感しました。

ここから、電話・Siri による中断、イヤホンの抜き差しによるルート変更を正しく扱い、再生が破綻しない実装を、Rork Max が生成するネイティブ Swift にそのまま組み込める形で見ていきます。

まずオーディオセッションのカテゴリを決める

何より先に、アプリがどういう音を出すアプリなのかを OS に宣言します。これを怠ると、サイレントスイッチで音が消えたり、他アプリの音楽を不必要に止めてしまったりします。

import AVFoundation
 
func configureAudioSession() {
    let session = AVAudioSession.sharedInstance()
    do {
        // 再生主体のアプリ。サイレントスイッチに影響されず再生する
        try session.setCategory(.playback, mode: .default)
        try session.setActive(true)
    } catch {
        print("Audio session setup failed: \(error)")
    }
}

.playback は「このアプリの音はコンテンツそのものである」という宣言で、ロック中やバックグラウンドでも鳴らせます。逆に、他アプリの音楽の上に短い効果音を重ねたいだけなら .ambient を選ぶ、というように、用途に応じてカテゴリを変えます。最初の宣言を正しく選ぶことが、後の挙動すべての土台になります。

中断通知を購読する

中断は AVAudioSession.interruptionNotification で届きます。.began(中断開始)と .ended(中断終了)の二つの局面があり、終了時には「自分から再開してよいか」のヒントが付いてきます。

final class PlaybackController {
    private var wasPlayingBeforeInterruption = false
 
    func observeInterruptions() {
        NotificationCenter.default.addObserver(
            self, selector: #selector(handleInterruption),
            name: AVAudioSession.interruptionNotification, object: nil)
    }
 
    @objc private func handleInterruption(_ note: Notification) {
        guard let info = note.userInfo,
              let raw = info[AVAudioSessionInterruptionTypeKey] as? UInt,
              let type = AVAudioSession.InterruptionType(rawValue: raw) else { return }
 
        switch type {
        case .began:
            // 電話やSiriが割り込んだ。状態を覚えておく
            wasPlayingBeforeInterruption = player.isPlaying
            player.pause()
 
        case .ended:
            guard let optsRaw = info[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
            let options = AVAudioSession.InterruptionOptions(rawValue: optsRaw)
            // shouldResume が立っていて、かつ中断前に再生中だったときだけ再開
            if options.contains(.shouldResume), wasPlayingBeforeInterruption {
                try? AVAudioSession.sharedInstance().setActive(true)
                player.play()
            }
 
        @unknown default:
            break
        }
    }
}

ここで肝になるのが shouldResume の扱いです。OS は「再開してよい中断」と「再開すべきでない中断」を区別しています。たとえば他の音楽アプリがユーザー操作で前面に出てきたときは、勝手に再生を奪い返すべきではありません。shouldResume が立っていないのに再生を再開すると、ユーザーの意図に反してしまいます。中断前に再生中だったかどうかも併せて確認し、両方を満たすときだけ静かに戻すのが行儀の良い実装です。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦AVAudioSession の中断通知(電話・Siri)とルート変更通知(イヤホン抜き差し)を正しく購読し、再生を破綻させない実装ができます
✦中断後に自分から再開してよい場合と、ユーザー操作を待つべき場合を shouldResume フラグで切り分けられます
✦Now Playing とリモートコマンドに連動させ、ロック画面やコントロールセンターから操作できる本番品質の音声アプリを組めます
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-07-18
文字サイズ最大とドイツ語で、生成された画面は静かに詰まる — Rork Max の SwiftUI にレイアウト耐性の検証を通した記録
Rork Max が生成した SwiftUI 画面を疑似ローカライズと最大文字サイズに通し、どこから詰まるかを機械的に洗い出した記録です。ViewThatFits・ScaledMetric・layoutPriority の使いどころと、再生成のたびに回帰を捕まえるスナップショット検証を、実際の数値とともにまとめました。
⬡ 開発ツール2026-05-17
Rork Max の SwiftUI 機能を壁紙アプリ開発で検証した結果 — 実際に動いた機能と手を入れた機能
運用中の壁紙アプリの本番コードを基準に、Rork Max の SwiftUI ネイティブ生成を検証しました。そのまま使えた機能、手を入れた機能、生成を諦めた機能を、動くコードと実機での計測値とともにお伝えします。
⬡ 開発ツール2026-05-12
Rork Max で複数アプリ共通UIライブラリを Swift Package Manager で構築する実践ガイド
複数アプリを運営する個人開発者向けに、Swift Package Managerで共通UIライブラリを構築してRork Max生成コードの重複を排除する手順です。パッケージ構成の設計、テーマシステムとカードコンポーネント、アニメーションプリセットの共通化、テストまで詳解します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます