0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MusicKitでApple Musicの再生履歴を取得する方法まとめ

Posted at

MusicKitで最近再生した曲の完全な情報を取得する方法

概要

MusicKit フレームワークには、ユーザーが Apple Music で最近聴いた曲やミュージックビデオを取得できる
MusicRecentlyPlayedRequest というAPIが存在します。
本記事では、その 基本的な使い方完全な楽曲情報を取得する方法(ハイドレーション) について解説します。

公式ドキュメント:
MusicRecentlyPlayedRequest | Apple Developer Documentation


基本的な使い方

以下は、ユーザーの許可を得てから最近再生した楽曲リストを取得し、レスポンスをそのまま print する最もシンプルな例です。

import MusicKit

func fetchAndPrintRecentlyPlayed() async throws {
    // ユーザーにApple Musicへのアクセス許可をリクエストする
    let status = await MusicAuthorization.request()
    guard status == .authorized else {
        print("Apple Musicへのアクセスが許可されていません。")
        return
    }

    // MusicRecentlyPlayedRequest<...> の部分で取得するデータ型を指定
    var request = MusicRecentlyPlayedRequest<Song>()
    
    // 取得件数の上限を設定 (最大値は30)
    request.limit = 25
    
    // 取得開始位置(ページネーション用)
    request.offset = 0

    // リクエストを実行してレスポンスを取得
    let response = try await request.response()
    print(response)
}

<T> に指定できるデータ型一覧

MusicRecentlyPlayedRequest<T><T> には、取得したいデータの型を指定します。

説明 ドキュメント
Song 個々の楽曲(ミュージックビデオは含まない)を取得 developer.apple.com
MusicVideo ミュージックビデオの履歴を取得 developer.apple.com
Track 楽曲(Song)とミュージックビデオ(MusicVideo)が混在した履歴を取得 developer.apple.com
Station ラジオステーションの再生履歴を取得 developer.apple.com
RecentlyPlayedMusicItem アルバム・プレイリストなどの「コンテナ」履歴を軽量に取得 developer.apple.com

レスポンスの特徴

上記の基本関数を実行しても、すべてのプロパティが返り値に含まれるわけではありません。

先ほどのswiftコードの実行例

MusicRecentlyPlayedResponse<Song>(
  items: [
    Song(id: "1821113648", title: "Episodes Intro", artistName: "Gucci Mane"),
    Song(id: "1846237407", title: "Fa Sure", artistName: "Money Man"),
    Song(id: "1846236924", title: "No Spaghetti", artistName: "Money Man"),
    Song(id: "1674326497", title: "Rasen in Okinawa", artistName: "Awich, Tsubaki, OZworld & CHICO CARLITO"),
    Song(id: "1834391398", title: "Hottoke", artistName: "Mukade"),
    Song(id: "i.dlvqRdJUzq3ozeK", title: "Champion Road", artistName: "BAD HOP"),
    Song(id: "1479862418", title: "Swing", artistName: "TOCCHI"),
    Song(id: "1526343785", title: "Tao", artistName: "Tsubaki"),
    Song(id: "i.9oJNbRBSPeo7Pgm", title: "Ainomamani (feat. Tsubaki)", artistName: "BASI"),
    Song(id: "1793303080", title: "Namuabidabutsu", artistName: "Tsubaki"),
    Song(id: "1472001765", title: "Merry Go Round (feat. BASI, Tsubaki, VIGORMAN & WILYWNKA)", artistName: "GeG"),
    Song(id: "1367480255", title: "Feel (feat. IO, Tubaki & Yo-Sea)", artistName: "DJ CHARI & DJ TATSUKI"),
    Song(id: "i.aJGY1m9T3PaK3dQ", title: "Moon (feat. Bon Iver)", artistName: "Daniel Caesar"),
    Song(id: "1836082423", title: "BAD LOVE", artistName: "HANA"),
    Song(id: "i.NJv0A49fl8zBlKQ", title: "Blue Jeans", artistName: "HANA"),
    Song(id: "1729761357", title: "Last Party Never End (feat. Tiji Jojo, YZERR, Vingo & Yellow Pato)", artistName: "BAD HOP")
  ]
)

ドキュメントを見るとSongには他のプロパティも含まれていますが、デフォルトではオプショナルになっていないフィールドのみが返り値に含まれます。

スクリーンショット 2025-10-19 0.58.48.png

これは 「遅延読み込み (lazy loading)」 のためです。
初回のレスポンスではリスト表示に必要な最低限のデータしか返されません。

duration, albums, artists などの詳細情報を取得するには、
.with() メソッドを使用して非同期的に追加取得(データハイドレーション)を行います。


完全な情報を取得する方法(ハイドレーション)

複数の曲を効率よくハイドレーションするには、
withThrowingTaskGroup を用いた並行処理が有効です。

import MusicKit

func fetchAndPrintRecentlyPlayedWithDetails() async throws {
    // Apple Musicアクセス権の確認
    let status = await MusicAuthorization.request()
    guard status == .authorized else {
        print("Apple Musicへのアクセスが許可されていません。")
        return
    }

    // 1. 再生履歴(サマリオブジェクト)を取得
    var request = MusicRecentlyPlayedRequest<Song>()
    request.limit = 25
    let response = try await request.response()

    // 2. 各楽曲の完全な情報を並行して取得(データハイドレーション)
    let detailedSongs = try await withThrowingTaskGroup(of: Song.self) { group in
        var songs: [Song] = []
        songs.reserveCapacity(response.items.count)

        // 取得したいプロパティのリスト
        let propertiesToLoad: [PartialMusicAsyncProperty<Song>] = [
            .artists,
            .albums,
            .duration,
            .genreNames,
            .composerName,
            .url
        ]

        // 各サマリオブジェクトに対して詳細取得タスクを追加
        for summarySong in response.items {
            group.addTask {
                try await summarySong.with(propertiesToLoad)
            }
        }

        // 全タスク完了後、結果をまとめる
        for try await detailedSong in group {
            songs.append(detailedSong)
        }

        return songs
    }

    // 3. 完全な情報を出力
    print("--- 取得した全楽曲の詳細情報 ---")
    print(detailedSongs)
    print("---------------------------")
}

propertiesToLoad に指定できる値

propertiesToLoad の部分で、ドキュメントページでオプショナルになっているフィールド名をセットするとその情報を含めた状態でのresponseを取得できます。


先ほどのswiftコードの出力例

.artists.albums を指定してハイドレーションした場合、
以下のようにネストされた完全な Song オブジェクトが取得されます。

--- 取得した全楽曲の詳細情報 ---
[
  Song(
    id: "1846237407",
    title: "Fa Sure",
    hasLyrics: false,
    isrc: "USUYG1765874",
    releaseDate: "2025-10-17",
    trackNumber: 2,
    url: "https://music.apple.com/jp/album/fa-sure/1846236922?i=1846237407&l=en-US",
    albums: [
      Album(
        id: "1846236922",
        title: "Money Man Essentials",
        // ... Albumの他のプロパティ
      )
    ],
    artists: [
      Artist(
        id: "689731655",
        name: "Money Man",
        // ... Artistの他のプロパティ
      )
    ]
  ),
  // ... 他の完全なSongオブジェクト
]
---------------------------

🎉 おしまい!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?