◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/アプリ開発
◇ アプリ開発/2026-07-01上級

Rork Max のネイティブ Swift で ShazamKit を使い、流れている曲を認識するアプリを作る

SHManagedSession を使って、周囲で流れている曲を認識するアプリを Rork Max のネイティブ Swift で組む実装メモです。旧来の AVAudioEngine 手回しとの違い、idle・prerecording・matching の状態設計、認識精度を上げる prerecording の使いどころ、Expo からの橋渡しの境界設計まで、実際にハマった点を添えてまとめます。

ShazamKitRork Max236音楽認識iOS115SwiftUI66

✦ プレミアム記事

友人と入った喫茶店で、耳になじむのに曲名が出てこない音楽が流れていて、二人でしばらく唸っていたことがありました。スマートフォンをかざせば一発で分かるあの体験を、自分の小さなアプリの一機能として持てたら面白いのに、とそのとき思いました。個人開発でユーティリティ系のアプリをいくつか作ってきましたが、「かざすと分かる」という手触りには、理屈抜きの気持ちよさがあります。

その入口になるのが ShazamKit です。Apple の音楽認識をアプリに組み込める仕組みで、周囲の音や自分のアプリの再生音から、曲を照合できます。かつては録音の面倒を自分で見る必要がありましたが、iOS 17 以降の SHManagedSession を使えば、その大半を任せられます。Rork Max がネイティブ Swift を生成するようになったことで、この API を個人開発でも素直に組み込めるようになりました。実際に「かざすと分かる」小さなアプリを作ってみて分かったことを、順を追ってまとめます。

手回しの旧来型か、任せる SHManagedSession か

ShazamKit には、大きく二つの組み方があります。ここを最初に選んでおくと、後の設計が決まります。

観点SHSession(手回し)SHManagedSession(iOS 17+)
録音の管理AVAudioEngine を自分で組むフレームワークに任せられる
マイク権限自分で要求・確認するセッションが面倒を見る
結果の受け取りデリゲートasync の結果列(results())
状態の可視化自前で管理idle / prerecording / matching を観測できる
向いている用途再生音との突き合わせなど細かい制御周囲の曲をかざして認識する定番用途

「周囲で流れている曲をかざして当てる」という王道の用途なら、迷わず SHManagedSession を選びます。録音とマイク権限の面倒を肩代わりしてくれるので、書く量が目に見えて減ります。私自身、最初は勉強のつもりで AVAudioEngine を手で組みましたが、権限まわりとバッファの取り回しで思いのほか時間を食い、SHManagedSession に切り替えたら本質だけが残りました。

状態を SwiftUI に映す

SHManagedSession は Observable に準拠しているので、状態の変化を SwiftUI がそのまま拾えます。状態は三つです。

  • idle:録音も照合もしていない、待機の状態
  • prerecording:照合に必要な準備が整い、先んじて録りためている状態
  • matching:実際に照合を試みている状態

この三状態をそのままボタンの見た目に反映すると、ユーザーに「いま何をしているか」が伝わります。以下は、状態に応じて表示を変える最小の画面です。

import SwiftUI
import ShazamKit
 
@MainActor
final class RecognizerModel: ObservableObject {
    let session = SHManagedSession()
    @Published var title: String?
    @Published var artist: String?
    @Published var isWorking = false
 
    /// かざして照合する。結果は results() の非同期列から受け取る。
    func recognize() async {
        isWorking = true
        defer { isWorking = false }
 
        // 照合を1回試み、最初の結果で打ち切る
        for await result in session.results {
            switch result {
            case .match(let match):
                if let item = match.mediaItems.first {
                    self.title = item.title
                    self.artist = item.artist
                }
                return                    // 当たったら止める
            case .noMatch:
                self.title = "一致なし"
                return
            case .error(let error, _):
                self.title = "エラー: \(error.localizedDescription)"
                return
            @unknown default:
                return
            }
        }
    }
}
 
struct RecognizeView: View {
    @StateObject private var model = RecognizerModel()
 
    var body: some View {
        VStack(spacing: 24) {
            if let title = model.title {
                Text(title).font(.title2).bold()
                if let artist = model.artist {
                    Text(artist).foregroundStyle(.secondary)
                }
            } else {
                Text(model.isWorking ? "聴いています…" : "タップして曲を認識")
                    .foregroundStyle(.secondary)
            }
 
            Button {
                Task { await model.recognize() }
            } label: {
                Image(systemName: "shazam.logo.fill")
                    .font(.system(size: 64))
            }
            .disabled(model.isWorking)
        }
        .padding()
    }
}

session.results は照合結果の非同期列を返します。当たった時点で return して抜けているのは、一曲当てられれば十分な用途だからです。連続して当て続けたいなら、抜けずに列を回し続ければ、流れる曲が変わるたびに結果が届きます。

✦

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

この記事の続きを読む

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

この記事で得られること
✦AVAudioEngine を手回しする旧来のやり方と、SHManagedSession に任せる新しいやり方の違い、どちらを選ぶかの判断基準が分かります
✦idle・prerecording・matching の状態を SwiftUI に映し、prerecording で初動の認識精度を上げる、動く Swift コード一式が手に入ります
✦Rork Max が生成するネイティブ Swift で ShazamKit を使う手順と、Expo / React Native から橋渡しする際の境界設計が身につきます
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

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

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

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

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

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

関連記事

◇ アプリ開発2026-04-26
Rork でネイティブ iOS アプリを作る — SwiftUI 生成から TestFlight 配信までの実践ワークフロー
Rork の新規 iPhone プロジェクトは Swift/SwiftUI で生成され、Rork Pro で App Store まで届きます。古い Expo プロジェクトとの見分け方、クレジットで見る費用、骨格プロンプト、実機確認、Xcode に降りる基準、TestFlight 提出までを手順で追います。
◇ アプリ開発2026-10-15
adjustsFontSizeToFit で特定の1行だけ極小になるとき、疑うのは文字列ではなく枠の高さです
一覧の10行目だけ文字が4ptまで縮む。データも文字列も正常なのに起きる iOS の症状を、Yoga の丸めと縮小判定のソースで確かめ、Node で再現率を数えた記録です。
◈ Rork 入門2026-09-08
Rork Max が要るかどうかは、料金表ではなく権限の棚卸しで判断できます
Rork Max に上げるべきか迷ったとき、料金表を先に見ないようにしています。app.json と Info.plist から実際に要求している OS 機能を抜き出し、標準版で届くもの・設定で届くもの・ネイティブが要るものへ仕分ける手順を書き残します。