ShazamKit Tutorial for iOS: Getting Started

Learn how to use ShazamKit to find information about specific audio recordings by matching a segment of that audio against a reference catalog of audio signatures. By Saleh Albuga.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Exploring ShazamKit Sessions

There are two steps left before you wire-up the UI. First, you need to implement SHSessionDelegate to handle matching successes and failures.

Add the following class extension at the end of MatchingHelper.swift:

extension MatchingHelper: SHSessionDelegate {
  func session(_ session: SHSession, didFind match: SHMatch) {
    DispatchQueue.main.async { [weak self] in
      guard let self = self else {
        return
      }

      if let handler = self.matchHandler {
        handler(match.mediaItems.first, nil)
        // stop capturing audio
      }
    }
  }
}  

In this extension, you implement SHSessionDelegate.
SHSession calls session(_:didFind:) when the recorded signature matches a song in the catalog. It has two parameters: The SHSession it was called from and an SHMatch object that contains the results.

Here, you check if the matchHandler is set, and you call it passing the following parameters:

  • The first SHMatchedMediaItem of the returned mediaItems in SHMatch: ShazamKit might return multiple matches if the query signature matches multiple songs in the catalog. The matches are ordered by the quality of the match, the first having the highest quality.
  • An error type: Since this is a success, you pass nil.

You’ll implement this handler block in SwiftUI in the next section.

Right after session(_:didFind:), add:

func session(
  _ session: SHSession, 
  didNotFindMatchFor signature: SHSignature, 
  error: Error?
) {
  DispatchQueue.main.async { [weak self] in
    guard let self = self else {
      return
    }

    if let handler = self.matchHandler {
      handler(nil, error)
      // stop capturing audio
    }                          
  }
}

session(_:didNotFindMatchFor:error:) is the delegate method SHSession calls when there’s no song in the catalog that matches the query signature or when an error that prevents matching occurs. It returns the error that occurred in the third parameter or nil if there was no match in the Shazam catalog for the query signature. Similar to what you did in session(_:didFind:), you call the same handler block and pass in the error.

Finally, to adhere to Apple’s microphone use guidelines and protect user privacy, you need to stop capturing audio when any of the two delegate methods are called.

Add the following method right after match(catalog:) in the main body of MatchingHelper:

func stopListening() {
  audioEngine.stop()
  audioEngine.inputNode.removeTap(onBus: 0)
}

Then, call stopListening() in both delegate methods above. Replace the following comment:

// stop capturing audio

with:

self.stopListening()

Next, you’ll display the matching result.

Displaying the Matched Song

The final part of your Shazam clone is the UI. Open SongMatchView.swift and check the preview in the canvas:

Simulator displays the app running

The view consists of two parts. The top part with the rounded green square is where you’ll show the song info. The bottom part has the Match button that starts the matching process.

First, you need a MatchHelper object. At the top of SongMatchView, add:

@State var matcher: MatchingHelper?

Then, at the end of the view struct, right after body, add:

func songMatched(item: SHMatchedMediaItem?, error: Error?) {
  isListening = false
  if error != nil {
    status = "Cannot match the audio :("
    print(String(describing: error.debugDescription))
  } else {
    status = "Song matched!"
    print("Found song!")
    title = item?.title
    subtitle = item?.subtitle
    artist = item?.artist
    coverUrl = item?.artworkURL
  }
}

songMatched(item:error:) is the method that MatchingHelper calls when it finishes matching. It:

  • Sets isListening to false. As a result, the UI updates to show the user that the app is not recording anymore and hides the activity indicator.
  • Checks the error parameter. If it isn’t nil, there was an error so it updates the status the user sees and logs the error to the console.
  • If there was no error, it tells the user it found a match and updates the other properties with the song metadata.
Note: SHMatchedMediaItem is a subclass of SHMediaItem. It inherits a media item’s metadata properties for the matched items, like title of the song, artist, genre, artwork URL and video URL.
It also has other properties specific to matched items like frequencySkew, the difference in frequency between the matched audio and the query audio.

Next, at the end of NavigationView, add:

.onAppear {
  if matcher == nil {
    matcher = MatchingHelper(matchHandler: songMatched)
  }
}
.onDisappear {
  isListening = false
  matcher?.stopListening()
  status = ""
}

Here you instantiate the MatchHelper passing the handler you just added when the view appears. When the view disappears, for example, when you switch to another tab, you stop the identification process by calling stopListening().

Finally, locate the Match button code, as below:

Button("Match") {
}
.font(.title)

In the button action block, add:

status = "Listening..."
isListening = true
do {
  try matcher?.match()
} catch {
  status = "Error matching the song"
}

This is where the magic starts. You change status to tell the user the app is listening and call match() to start the matching process. When SHSession returns a result to MatchingHelper, it calls songMatched(item:error:).

Next, you’ll test the app.

Testing The App

ShazamKit App Service

After that, set the bundle identifier and signing settings accordingly.

Note: At the time of writing, you can only test ShazamKit on a physical device. You’ll also need an Apple Developer account in order to register an App ID with the ShazamKit App Service. You must do this configuration manually on the Apple Developer Portal:

ShazamKit App Service

After that, set the bundle identifier and signing settings accordingly.

To try matching a song, build and run the app on an iPhone.

DevCompanion running

Open the following YouTube link to play the song. Can you guess the song?

Tap Match and bring your iPhone closer to the speakers. A few seconds later, you’ll see the match:

DevCompanion showing matched song

Hooray! The app successfully matched the song.

Working With Custom Catalogs

You learned how to use ShazamKit to match audio against the Shazam catalog. What if you wanted to match your music compositions or video content? ShazamKit’s got you covered.

Now you’ll implement the remaining part of DevCompanion, the Video Content tab. You’ll start by matching audio against a custom catalog you’ll create.

The app will identify the intro video of Your First iOS and SwiftUI App: An App From Scratch video course. This is an amazing free video course for learning the fundamentals of SwiftUI.

Before you do that, you need to learn more about Shazam Signature files.