이전 글
[WWDC23] Create a great ShazamKit experience (1)
ManagedSession에 대해 더 살펴보면,
상황에 따라 ManagedSession이 미리 매치를 시도하도록 준비할 수 있어요.
미리 준비했을 때 좋은 점은
이 정도 장점이 있겠네요 :)
그럼 preparing 있을 때와 없을 때 어떻게 다른지 한 번 볼게요.

코드로 한 번 살펴볼까요?
결과 요청하기 전에 prepare()를 호출하면 됩니다.
let managedSession = SHManagedSession()
// prepares the session for a match attempt
await managedSession.prepare()
let result = await managedSession.result()
이 메소드 호출 여부는 전적으로 개발자한테 달려있어요.
필요하다면 ShazamKit이 대신 호출할 수도 있구요.
이쯤 되면 한 가지 질문이 떠오를텐데요.
만약 세션이 길게 유지되는 경우에, 지금 레코딩 중인지 아니면 매칭 중인지 아니면 또 다른 업무 중인지 등 그 상태를 우리가 어떻게 파악할 수 있냐는 겁니다.
이에 대한 해답을 주기 위해, Managed Session이 state 속성을 갖고 있습니다.
state에는 세 가지 속성이 있어요.
실제로 state를 어떻게 사용하는지 코드로 살펴볼까요?
struct MatchView: View {
let session: SHManagedSession
var body: some View {
VStack {
Text(session.state == .idle ? "Hear Music?" : "Matching")
if session.state == .matching {
ProgressView()
} else {
Button {
// start match
} label: {
Text("Learn the Dance")
}
}
}
}
위에서 작성한 코드는 SwiftUI 코드인데, SwiftUI가 자동으로 뷰를 갱신하고 개발자의 별다른 작업없이 즉각 변화를 반영하도록 해줍니다 !
아주 편리하죠 ?
이게 가능한 이유는, 또 다른 WWDC23 영상에서 다루고 있는데요.
managedSession이 Observable을 따르고 있기 때문에 가능하다고 해요.
그렇기 때문에 managedSession의 state에 어떠한 변화가 있다고 해도 SwiftUI가 바로 반응할 수 있는 것이죠.
이에 대한 자세한 내용은 [WWDC23] Discover Observation in SwiftUI에서 확인할 수 있습니다.
2021년, ShazamKit는 개발자들이 유효한 shazam ID를 가지고 있다면, 일치 결과를 shazam 라이브러리에 쓸 수 있도록 API를 제공했습니다.
이는 곧 Shazam 카탈로그에 있는 노래와 일치한다는 것을 의미합니다.
추가된 항목은 컨트롤 센터에 있는 Music Recognition 모듈에 보일 뿐만 아니라 설치가 되어 있다면 Shazam 앱에서도 보입니다.
또한 모든 디바이스에 동기화되어 있죠.
특별한 권한 없이도 Shazam 라이브러리에 쓸 수 있는데요.
고객이 모르는 사이에 컨텐츠는 저장하는 일은 있어서는 안된다고 덧붙이네요.
몇 년동안, 이 API는 다양하게 사용됐고 몇 가지 단점을 발견했어요.
그 중 하나가, 사용자가 추가한 항목을 오직 컨트롤 센터의 기록에서만 확인할 수 있다는 것입니다.
이에 대한 솔루션으로 개인 로컬 스토리지에 매치 결과를 저장하자를 떠올렸지만, 다루기 번거롭고 버그가 쉽게 발생하는 그런 솔루션이죠 🥲
그래서 나온 또 다른 솔루션이 SHLibrary라 불리는 새로운 class입니다.
SHLibrary를 사용하면, 이전에 사용하던 SHMediaLibrary와 비교해서 더욱 광범위한 기능을 사용할 수 있습니다.
SHLibrary가 가진 코어 기능 중 몇 가지를 살펴볼까요?
앱은 라이브러리에 추가된 내용만 읽고 삭제할 수 있다.
읽을 때 반환되는 항목은 앱에만 해당되며 라이브러리 전체를 나타내는 것은 아닙니다.
또한 앱이 추가하지 않은 항목을 삭제하려고 하면 오류가 발생합니다.
이제 SHLibrary를 어떻게 사용하는지 알려드릴게요.
func add(mediaItems: [SHMediaItem]) async throws {
try await SHLibrary.default.addItems(mediaItems)
}
이 add 메소드는 배열 형태인 mediaItems를 추가하는 동작을 수행해요.
struct LibraryView: View {
var body: some View {
List(SHLibrary.default.items) { item in
MediaItemView(item: item)
}
}
}
SHLibrary는 Swift의 Observable 타입을 따르고 있어요.
그렇기에 앞에서도 언급했듯이, SwiftUI View는 변화가 생기면 자동으로 reload 됩니다 ! 개발자의 개입이 없더라도요 ㅎㅎ
또한 UI가 아닌 상황에서도 라이브러리에서 아이템을 읽어올 수 있어요.
만약 내가 동기화된 Shazam에서 가장 인기있는 장르의 사용자를 검색하고 싶으면, Library의 현재 아이템을 요청할 수도 있어요.
말이 쪼금 어렵죠? 코드로 살펴볼까요?
let currentItems = await SHLibrary.default.items
let genres = currentItems.flatMap { $0.genres }
let mostPopularGenre = highestOccurringGenre(from: genres)
아이템 삭제도 매우 쉬운데요.
library의 removeItems를 호출해주면 끝입니다.
func remove(mediaItems: [SHMediaItem]) async throws {
try await SHLibrary.default.removeItems(mediaItems)
}
SHManagedSession과 SHSession은 매우 관련이 깊은데요.
상황에 맞게 둘 중 한 타입을 선택해서 사용하면 좋을 것 같아요.
두 타입은 공동의 목표를 가지고 있으나 전혀 다른 방향으로 접근하고 있어요.
| SHManagedSession | SHSession |
|---|---|
| - ShazamKit이 recording을 핸들링하게 하고 싶을 때 | - 내가 오디오 버퍼를 만들고 프레임워크에 이걸 던져주고 싶을 때 |
| - 마이크나 에어팟에서 넘어온 오디오를 인식하고 싶을 때 | - 오로지 마이크로 넘어온 오디오만 인식하고 싶을 때 |
| - 시그니처 파일로 매칭 ❌ | - 시그니처 파일을 갖고 있거나 메모리에 시그니처 파일이 있을 때 |
| - ShazamKit이 오디오 포맷 자동으로 결정 | - 여러개의 PCM 오디오 포맷 중에서 선택 |
SHSession의 오디오 포맷에 대해서도 잠깐 얘기해볼게요.
이전에는 matchStreamingBuffer 메소드는 오로지 아래 보이는 특정 포맷과 PCM 오디오 버퍼를 매칭할 수 있도록 했어요.

지원하지 않는 설정의 오디오 버퍼는 NoMatch 결과로 나온답니다.
이번 발표를 통해, SHSession은 이제 대부분의 포맷을 지원하게 되었답니다.
오디오를 넘기면 자동으로 오디오 포맷 변환을 한 다음 SHSession으로 넘어가요.
마지막으로 넘어온 쿼리 시그니처가 커스텀 카탈로그에서 2비트 이상 오디오가 비슷하면, ShazamKit은 그 일치하는 모든 결과들을 리턴할겁니다.
매칭 결과가 반환되면, 가장 퀄리티 높게 매치되는 결과대로 정렬을 해줄테고 그 중 가장 적절한 것으로 필터링해서 사용할 수 있습니당.

영상 내내 다뤘던 데모 앱을 계속해서 데이비드가 발전시키는데요.
직접 영상 보시면, 설명했던 내용을 데모 앱에 적용하고 보여주기도 해서 더 재밌을 거에요.
스와이프 동작으로 아이템 삭제하기, 여러 디바이스 싱크 맞추기 등 흥미롭답니다 😊
그리고 데이비드는 SHManagedSession으로 앱을 만들었기 때문에 에어팟으로도 오디오 인식이 가능해요.
그래서 마지막에 에어팟 끼고 노래 인식하는 부분도 있으니 영상 꼭 시청해보시면 좋을 것 같아요 ! 👋🏻