RORK LABEN
MAX — Rork MaxがネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますNATIVE — AR/LiDAR、Metalによる3D、Dynamic Island、Live Activities、HealthKit、Core MLなどに手が届きますPUBLISH — 2クリックでApp Storeへ提出でき、申請まわりの手間が大きく圧縮されていますPRICING — Rork Maxは月200ドル、元のRorkは無料で始められ有料は月25ドルからですFUNDING — Rorkがa16zから280万ドルを調達し、月間74万超の訪問・成長率85%と紹介されていますTOOL — 元のRorkはReact Native(Expo)でiOSとAndroidのアプリを自然言語から生成しますMAX — Rork MaxがネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますNATIVE — AR/LiDAR、Metalによる3D、Dynamic Island、Live Activities、HealthKit、Core MLなどに手が届きますPUBLISH — 2クリックでApp Storeへ提出でき、申請まわりの手間が大きく圧縮されていますPRICING — Rork Maxは月200ドル、元のRorkは無料で始められ有料は月25ドルからですFUNDING — Rorkがa16zから280万ドルを調達し、月間74万超の訪問・成長率85%と紹介されていますTOOL — 元のRorkはReact Native(Expo)でiOSとAndroidのアプリを自然言語から生成します
記事一覧/アプリ開発
アプリ開発/2026-07-01上級

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

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

ShazamKitRork Max199音楽認識iOS92SwiftUI57

プレミアム記事

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

その入口になるのが 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 に映す

SHManagedSessionObservable に準拠しているので、状態の変化を 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-06-28
Rork Max でバーコードとテキストを同時に読むスキャナを VisionKit で実装する
Rork Max のネイティブ Swift アプリに、バーコードとテキストをライブで読み取る画面を VisionKit の DataScanner で実装します。SwiftUI への組み込み、可用性判定、連続検出のスロットリング、実機検証までを実装コード付きで扱います。
アプリ開発2026-07-01
Rork Max のネイティブ Swift で AlarmKit のタイマーを実装する — 消音・集中モードを越えて鳴らす
ローカル通知が消音モードや集中モードで鳴らない問題を、iOS 26 の AlarmKit で解決する実装メモです。認可・カウントダウンの schedule・ロック画面と Dynamic Island の Live Activity・実行中タイマーの一覧までの Swift コードと、Rork Max のネイティブ生成や Expo からの橋渡しの境界設計を、実際にハマった点を添えてまとめます。
アプリ開発2026-06-30
ユーザー投稿画像を表示前に端末内で判定する — SensitiveContentAnalysis 実装メモ
AI 生成画像やユーザー投稿を扱うアプリで、不適切な画像を表示前に端末内で弾くための実装メモです。Apple の SensitiveContentAnalysis フレームワークを Swift で呼び、Rork Max が生成したネイティブコードや Expo のネイティブモジュールへ組み込むところまでを、実際にハマった点を添えて解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →