Recognize music and audio with ShazamKit using SHManagedSession, SHSession, SHCustomCatalog, and SHSignatureGenerator for live microphone matching and custom audio catalogs in iOS apps.
81%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Match recorded or ambient audio against the Shazam catalog or custom audio catalogs using SHManagedSession, SHSession, and SHCustomCatalog in iOS and iPadOS.
On iOS 17+, SHManagedSession encapsulates audio recording, session management, and recognition into an async stream of SHManagedSession.Item results.
import ShazamKit
@MainActor
@Observable
final class AudioRecognitionViewModel {
private let managedSession = SHManagedSession()
var currentMatch: SHMatchedMediaItem?
var isListening = false
func startListening() async {
isListening = true
defer { isListening = false }
for await item in managedSession.results {
switch item {
case .match(let match):
if let mediaItem = match.mediaItems.first {
self.currentMatch = mediaItem
}
case .noMatch:
// Signature didn't match any known audio
break
@unknown default:
break
}
}
}
func stopListening() {
managedSession.cancel()
isListening = false
}
}When manual control over the AVAudioEngine pipeline or custom catalogs is required, feed PCM buffers directly to SHSession.
import ShazamKit
import AVFAudio
final class ManualShazamMatcher: NSObject, SHSessionDelegate, @unchecked Sendable {
private let session: SHSession
private let engine = AVAudioEngine()
init(catalog: SHCustomCatalog? = nil) {
if let catalog {
self.session = SHSession(catalog: catalog)
} else {
self.session = SHSession()
}
super.init()
self.session.delegate = self
}
func startMatching() throws {
let inputNode = engine.inputNode
let format = inputNode.outputFormat(forBus: 0)
inputNode.removeTap(onBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 2048, format: format) { [weak self] buffer, audioTime in
self?.session.matchStreamingBuffer(buffer, at: audioTime)
}
try engine.start()
}
func stopMatching() {
engine.inputNode.removeTap(onBus: 0)
engine.stop()
}
// MARK: - SHSessionDelegate
func session(_ session: SHSession, didFind match: SHMatch) {
guard let item = match.mediaItems.first else { return }
print("Matched: \(item.title ?? "Unknown") by \(item.artist ?? "Unknown")")
}
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
// No match found
}
}Match non-commercial audio, museum exhibits, podcast episodes, or proprietary assets by building an SHCustomCatalog.
import ShazamKit
final class CustomCatalogService {
let catalog = SHCustomCatalog()
func addTrack(signature: SHSignature, title: String, artist: String) throws {
let mediaItem = SHMediaItem(properties: [
.title: title,
.artist: artist
])
try catalog.addReferenceSignature(signature, representing: [mediaItem])
}
func exportCatalog(to fileURL: URL) throws {
try catalog.write(to: fileURL)
}
func loadCatalog(from fileURL: URL) throws {
try catalog.add(from: fileURL)
}
}Generate compact, privacy-preserving audio signatures from PCM buffers without sending raw audio to servers.
import ShazamKit
import AVFAudio
final class SignatureGeneratorHelper {
func generateSignature(from audioFile: AVAudioFile) throws -> SHSignature {
let generator = SHSignatureGenerator()
let format = audioFile.processingFormat
let buffer = AVAudioPCMBuffer(
pcmFormat: format,
frameCapacity: AVAudioFrameCount(audioFile.length)
)!
try audioFile.read(into: buffer)
try generator.append(buffer, at: nil)
return generator.signature()
}
}Info.plist crashes at launch.SHSession triggers session(_:didNotFindMatchFor:error:) frequently during silence or unrecognized songs; handle this without treating it as a fatal failure.generator.signature() is called, the signature generator is finished; create a new instance for subsequent audio segments.AVAudioEngine to prevent runtime crashes.NSMicrophoneUsageDescription declared in Info.plist for live listening?SHManagedSession used for straightforward iOS 17+ ambient audio recognition?session.matchStreamingBuffer(_:at:)?SHSessionDelegate implement both didFind and didNotFindMatchFor?.shazamcatalog files or initialized with valid reference signatures?engine.inputNode.removeTap(onBus: 0) called when listening concludes?thienqo/ios-skills
6bf8b62
Also appears in
since Sep 6, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.