ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
MusicKit for Swiftについて
MusicKitによって、Apple MusicをAppに簡単に統合することができます。このSwiftベースのフレームワークにおける、コンテンツの検索、リクエスト、再生など、MusicKitの基本的な使い方を説明します。また、Apple Musicにまだ登録していない人がいる場合に、音楽サブスクリプションのワークフローをAppに組み込む方法についても解説します。
リソース
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
WWDCへようこそ Joelです 本日はMusicKitを使い Appに音楽を追加する 方法についてお話しします MusicKitはAppleプラットフォームの 新フレームワークで AppがSwiftの音楽に アクセスするための エクスプレッシブAPIを 提供します これはSwiftUIと併用するために 一から設計された 新しいSwift並行構文を 活用するものです MusicKitはAppが Apple Musicの多様な カタログコンテンツに アクセスするための サーバーサイドAPIである Apple Music APIとの 連携を加速するため Apple Musicに直結する 魅力的なAppの 構築をより簡単にします まず MusicKitで音楽コンテンツを リクエストする方法についてお話しします 次にAppがApple Musicと 連携する方法に関して 重要な以下のその他の トピックに関してお話します AppがApple Music関連 データにアクセスするための ユーザーの同意のリクエスト方法や Apple Music APIのアクセスに 必要なトークンの管理方法 サブスクリプション情報と 関連機能へのアクセス方法 Apple Musicカタログからの 音楽の再生方法 そして最後にユーザーがまだ サブスクリプションしていない場合に Apple Musicのサブスクリプション オファーを表示する方法です MusicKitは音楽アイテムのアクセス用の 新しいモデルレイヤと Apple Music APIからの コンテンツのフェッチを 許可する構造化リクエストを 提供します Apple Musicカタログで コンテンツを検索するか 特定のフィルタに基づき リソースをフェッチできます これらのリクエストは ページネーションの サポートが内蔵され 初期応答からアイテムの 次のバッチを取得する グループ化されたアイテムの コレクションとして 応答を生成します ではこの音楽アイテムとは どういうものでしょう? Albumを使って 具体的な例を見てみましょう “Album”とは値型でプロパティが 3つのカテゴリに分類されています 最初のカテゴリには次の 簡単な属性が含まれています “title”などの 文字列プロパティ “isCompilation”などの ブーリアンプロパティ あるいは アートワークのURLや 関連のサイズ 色などの情報に アクセスできるartwork のような より構造化されたプロパティ があります アルバムは 関連の アーティストやジャンル 特定のアルバムの曲目など いくつかの関係も提供します たとえば“tracks”の関係の結果は 別の音楽アイテムのタイプである “track”タイプの値のコレクションです 最後に これらの 強いモデルレベルの関係に加えて “Album”は関連コンテンツの 弱い関連付けも提供します 関連付けは関係と類似しますが 通常 一時的なものか エディターベースのものです たとえば あるアルバムの ”appearsOn"の関連付けは プレイリストのコレクションを返しますが 関係のコレクションとは異なり タイトルも含まれています MusicKitを使用した 関係の読み込みと アクセスは非常に簡単です アルバムの“artists”や “tracks”などの関係を含む この同じアルバムの 別の表現や “related albums”などの 関連付けを 単一の演算で 簡単にフェッチできます “with”メソッドは“await”という 特別なSwiftキーワードで 呼び出す必要があり 裏で非同期演算を行うことを 示しています これはApple Music APIから ネットワーク経由で このアルバムのより完全な 表現をフェッチします 次に この詳細なアルバムから 曲目を取得し 通常の配列のように これらの曲を反復します こちらがこのコードの コンソール出力です “related albums”などの 関連付けへのアクセスは 同じように機能しますが 唯一の違いは 通常 直接コレクションからアクセス可能な タイトルが含まれている点です コレクションを 同じ方法で反復すれば 関連のアルバムを プリントできます こちらがこのコードの コンソール出力です では MusicKitで 音楽のコンテンツを リクエストする デモを見てみましょう Apple Musicから アルバムを検索して 楽しめるAppを作成中です 検索フィールドを使って アルバムを検索できます これは音楽カタログ 検索リクエストを使い 一致する検索結果を 読み込むコードに すでに接続されています このAppは最近表示した アルバムのリストを 記録します “Catch A Vibe - EP”という アルバムの情報を得るには これを選択します そうすると このアルバムの曲目が この詳細ビューが 表示された後に表示されます これはこのアルバムの “tracks”関係を 読み込むことで行われ SwiftUIビューの 状態変数を更新し このリストを追加するのに 使用されます アートワークの下にある 再生ボタンを押すか 特定の曲を選択すれば このアルバムを 聴くことができます これはMusicKitの 再生APIを使用し この曲目でキューを設定し プレイヤーの再生メソッドを 呼び出します では試してみましょう Karun & MONBRUの “Catch a Vibe”という 曲を選択します 曲が再生されています ♪ オー オー ノー ♪ このAppはロック画面の メディアコントロールで 自動的に動作し 曲の真ん中まで 早送りできます
しかし僕のやりたいことは CDライブラリから たとえば このPhoenixの アルバムなど 古い音楽を再発見することです なのでiPhoneのカメラを 古いCDのバーコードに向け Appがこのアルバムの デジタル版を 見つける機能を 追加したいと思います この機能の実験的なコードは すでに追加しています これを有効にすると 下部にバーコードボタンが 表示され カメラビューを呼び出します このCDのバーコードに カメラを向けると 自動的にバーコード値を 認識して表示します あとはMusicKitを使用して 同じアルバムを検索する コードが必要なだけです ではそれをAppに 追加しましょう MusicCatalogResourceRequestで albumsRequestを行います 探したいのはアルバムです
ここでは バーコードの技術的用語 Universal Product Code いわゆるUPCプロパティが equalTo: detectedBarcode のアルバムを検索します これを非同期でリクエストできます
albumsRequest.response
そしてこの応答から 検出された最初のアルバムを 見ます
次のこの最初のアルバムを 下部のヘルパーメソッド handleDetectedAlbumに パスします
handleDetectedAlbum (firstAlbum)
このメソッドはバーコード スキャンビューを却下し 検知されたアルバム詳細 ビューをプッシュします
そしてメインスレッドで 実行されるよう MainActorで装飾されます ですので これを呼び出す場合 awaitキーワードを 必ず追加しましょう
これを構築し 再び Appで実行してみましょう
バーコードボタンをタップし アルバムをかざすと
成功しました!
これでApple Music上で 昔の音楽を デジタル形式で簡単に 楽しめるようになりました MusicKitは一般的な目的の データリクエストも提供し 構造化されたリクエストとは異なり 独自のURLを使用して 任意のApple Music APIエンドポイントから コンテンツを読み込むことができます
このリクエストで得るのは Apple Music APIからの JSON応答の生データです これをJSONDecoderで 解読する必要がありますが 難しくはありません なぜなら 既存の 音楽アイテムタイプが Codableプロトコルと一致し 活用できるからです 例を見てみましょう Apple Musicの人気のジャンルを リストで表示したい場合 この特定のURLで コンテンツを 読み込むことができます こちらが対応するJSON応答です この結果を詳しく見ると ジャンルのリソースのような ものが真ん中にあります では これをSwiftで表現するには どうすればいいでしょう 案の定MusicKitには Genreタイプがあります では応答全体を Swiftで表現するには? ジャンルの簡単な配列である データメンバーで構造体を作成できます この構造体を必ず Decodableにしてください これには追加のデコードロジックを 書く必要はありません “Genre”自体がDecodableに 適合するからです このデータをAppに読み込むには この構造体をファイルの 最上部に配置し ユーザーの国コードを使用して URLを構築します 先ほどと同じパターンに従って このURLを使用して 音楽データを作成し そこから応答を得ます 応答を得たら デコードメソッドに MyGenresResponseタイプで パスし JSONDecoderで データを解読します 以上です! これで強く型付された ジャンル応答で 個々のジャンルに アクセスできます ご覧のとおりMusicKitの 他のリクエストで フェッチできるのと同じ 音楽アイテムを取得できます これがApple Music APIの 任意のURLから 音楽コンテンツを 読み込む方法でした 音楽アイテムの読み込み 方法がわかったところで AppとApple Musicの 連携における 重要な事前ステップについて お話しします まずはプライバシーから どのAppがユーザーの個人情報に アクセスするのかを ユーザーが管理できることが重要です ユーザーの再生履歴や ミュージックライブラリを 含むApple Music APIのデータを リクエストする前に AppがApple Musicに アクセスするためには ユーザーのインフォームド コンセントが必要です このユーザーの同意はデバイスごと かつAppごとに得る必要があります これはZovaというAppでの 同意に関するダイアログの例です Apple Musicまたは ユーザー自身のプレイリストで 音楽を再生しながら ワークアウトができる すばらしいAppです これを使用して 初めてワークアウトする時 ZovaはApple Musicへの アクセスをリクエストします このダイアログでは なぜAppが Apple Musicにアクセスする 必要があるのか 理由を 伝えなければいけません そのため Appの Apple Musicの使用説明を Info.plistで 定義する必要があり このダイアログのサブタイトル として表示されます これはMusicKit向けの ユーザー同意を リクエストする例です Appに MusicKitを必要とする 機能があるとします そして isAuthorizedForMusicKit 状態変数を使用し この機能へのアクセスを ゲーティングするとしましょう MusicKitの使用を試みる前に 適切なポイントで この非同期の リクエストメソッドを使用し Apple Musicへのアクセスの 許可をリクエストします これはユーザーが まだ同意していない場合のみ プロンプトされます リクエストメソッドは 状態値を返し 状態が“authorized”に等しい場合 isAuthorizedForMusicKit 変数を“true”に設定します Apple Music APIから データを読み込む際に 必要なトークンについて 簡単に説明します Apple Music APIには AppをAPIで認証する デベロッパトークンが必要です 以前 このデベロッパ トークンを得るには デベロッパポータルで MusicKit秘密鍵を 作成して サーバーに配備し 鍵の非公開に保つため 自己管理し サーバから新しい デベロッパトークンを リクエストする必要が ありました しかし現在 SwiftのためのMusicKitなら デベロッパトークンはAppで 自動的に生成されるため これらについて心配する 必要がなくなりました 単にデベロッパポータルに登録して この自動動作を オプトインするだけです 具体的に説明すると App IDを登録したページで 下部のApp Servicesを選択し 「MusicKit」チェックボックスを 有効にすれば 完了です さらにApple Music APIは パーソナライズされた エンドポイントに トークンを必要とします デベロッパトークンと同様 今年 ユーザートークンも自動的に 生成されるようになりました AppでのMusicKitの使用で もう1つ必要なのは ユーザーがApple Musicを サブスクリプション登録しているか どうかを知る方法です MusicKitの サブスクリプション情報は 3つの特徴的な特性で 把握されます ユーザーがApple Musicカタログから 音楽を再生できるか iCloud Music Libraryが 有効になっているか そして 有効なサブスクリプションがない場合 サブスクリプション登録者に なれるかどうかです
AppのApple Music関連の機能に適した 特性を確認してください たとえば音楽を再生するための 再生ボタンがある場合 ユーザーがApple Musicの カタログコンテンツを 再生できない場合 ボタンを無効にするべきです ビューの状態変数を定義し 音楽サブスクリプションを 追跡できます そして無効化の修飾子を ボタンに適用し 音楽サブスクリプションプロパティの "canPlayCatalogContent" が“false”なら 無効のままになるようにします そして最後に 新規タスク修飾子にパスされた 非同期ブロックの中で 新しいサブスクリプション 更新ストリームを使用して 音楽サブスクリプションの変更の 通知を受けることができます 次はMusicKitを使った 再生についてです MusicKitは2つの プレイヤーを提供します SystemMusicPlayerと ApplicationMusicPlayerです 例を使ってこれらの違いを 説明しましょう ソーシャルメディアAppは システム音楽Appで再生される音楽を SystemMusicPlayerで 変更することを好み フィットネスAppは システム音楽Appから 完全に独立して 再生状態を維持するため ApplicationMusicPlayerを 好む場合があります どちらのプレイヤーも自動的に “再生中”の情報を報告し 遠隔コマンドを処理します これが前半でお話しした ロック画面でシステム メディアコントロールとの 深い連携を可能にしました しかし“再生中”Appは 異なるものを報告しています SystemMusicPlayerを 使用した場合 Music Appが“再生中”Appとして 報告されます ApplicationMusicPlayerを 使用した場合 皆さんのAppが“再生中”Appとして 報告されます 再生キューの所有権も異なります SystemMusicPlayerでは 皆さんのAppは単にシステムの Music Appのリモコン的役割ですが ApplicationMusicPlayerでは 完全に異なる再生キューを 所有しています どちらのプライヤーも 1つ以上のアイテムを キューに設定し 次に再生や後で再生に追加できます ただし 真ん中にアイテムを挿入したり 以前追加したアイテムを 削除したりするなどの 追加のコントロールが行えるのは ApplicationMusicPlayerのみです 最後に ユーザーがまだ Apple Musicの登録者でない場合 デベロッパのApp内でApple Musicを 無料で試してもらうことができます そうすれば皆さんが努力して 改善したすべての機能を 試してもらうことができます サブスクリプションオファーは 曲の再生などの Appの機能性をうまく 伝えるために ユーザーへの メッセージを調整して 提供できます
さらにコンテクストを加えて 特定の曲やアルバム プレイリストを ハイライトすることもできます
Appでサブスクリプション オファーシートを使用することで Apple Musicの 新規登録者の獲得時に Apple Services Performance Partners Programという 提携プログラムを通して リワードがもらえます App内の音楽サブスクリプション オファーにコンテクストを 追加するには 先ほど紹介したように 音楽サブスクリプションを 追跡する必要があります またオファーが表示されているかを 追跡するために 状態変数が必要となります アルバムのIDをサブスクリプション オファーオプションの itemIDプロパティに パスするとしましょう 音楽サブスクリプションで canBecomeSubscriberが “false”の場合は オファーボタンを無効にします 次に isShowingOfferプロパティに バインディングした musicSubscriptionOffer 修飾子を使用し オプションも含みます 最後にisShowingOffer 変数を“true”にします Appに戻ってApple Musicの コンテクストを追加した オファーを見てみましょう デモの前半ですでに有効な Apple Musicサブスクリプションに サインインしています Apple Music サブスクリプションオファーの シナリオを再現するために 設定画面に移動し アカウントからサインアウトします
Appに戻ると再生ボタンが 無効になりApple Musicに ユーザーを招待するための ボタンが追加されています このボタンをタップすると サブスクリプションオファーが 表示され Appで先ほど見た アルバムカバーが ハイライトされています このようにして ユーザーが皆さんのApp内で Apple Musicの 無料トライアルを開始できます 結論として 多くのAppは 音楽を追加することで ユーザー体験を強化できます たとえばゲームのムードに合った BGMを再生して より没入型のゲーム体験にしたり テンポの早い音楽で フィットネスAppの ユーザーにやる気を 起こさせたりできます またソーシャルメディアAppでは 音楽でアクセントを加えた コンテンツでユーザー エンゲージメントを高められます
詳細については 関連のセッションをご覧ください ShazamKitでShazamのパワーを 最大限活用する方法や SwiftUIの並行処理に ついて深く学べます ご清聴ありがとうございました [パーカッシブな音楽]
-
-
2:56 - Loading and accessing relationships
// Loading and accessing relationships let detailedAlbum = try await album.with([.artists, .tracks, .relatedAlbums]) print("\(detailedAlbum)") if let tracks = detailedAlbum.tracks { print(" Tracks:") tracks.prefix(2).forEach { track in print(" \(track)") } }
-
3:31 - Loading and accessing associations
// Loading and accessing associations let detailedAlbum = try await album.with([.artists, .tracks, .relatedAlbums]) print("\(detailedAlbum)") if let relatedAlbums = detailedAlbum.relatedAlbums { print(" \(relatedAlbums.title ?? ""):") relatedAlbums.prefix(2).forEach { relatedAlbum in print(" \(relatedAlbum)") } }
-
9:02 - Loading top level genres
// Loading top level genres struct MyGenresResponse: Decodable { let data: [Genre] } let countryCode = try await MusicDataRequest.currentCountryCode let url = URL(string: "https://api.music.apple.com/v1/catalog/\(countryCode)/genres")! let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) let dataResponse = try await dataRequest.response() let decoder = JSONDecoder() let genresResponse = try decoder.decode(MyGenresResponse.self, from: dataResponse.data) print("\(genresResponse.data[9])")
-
10:49 - Requesting user consent for MusicKit
// Requesting user consent for MusicKit @State var isAuthorizedForMusicKit = false func requestMusicAuthorization() { detach { let authorizationStatus = await MusicAuthorization.request() if authorizationStatus == .authorized { isAuthorizedForMusicKit = true } else { // User denied permission. } } }
-
12:54 - Using music subscription to drive state of a play button
// Using music subscription to drive state of a play button @State var musicSubscription: MusicSubscription? var body: some View { Button(action: handlePlayButtonSelected) { Image(systemName: "play.fill") } .disabled(!(musicSubscription?.canPlayCatalogContent ?? false)) .task { for await subscription in MusicSubscription.subscriptionUpdates { musicSubscription = subscription } } }
-
15:34 - Showing contextual music subscription offer
// Showing contextual music subscription offer @State var musicSubscription: MusicSubscription? @State var isShowingOffer = false var offerOptions: MusicSubscriptionOffer.Options { var offerOptions = MusicSubscriptionOffer.Options() offerOptions.itemID = album.id return offerOptions } var body: some View { Button("Show Subscription Offers", action: showSubscriptionOffer) .disabled(!(musicSubscription?.canBecomeSubscriber ?? false)) .musicSubscriptionOffer(isPresented: $isShowingOffer, options: offerOptions) } func showSubscriptionOffer() { isShowingOffer = true }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。