{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-254", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/254", "wordCount": 2562, "segments": [{"start": 7.0, "text": "Hi, and welcome to WWDC 2026."}, {"start": 12.0, "text": "I'm Cathy, an engineer on the MusicKit team!"}, {"start": 16.0, "text": "Today, my teammate Alan and I want to demonstrate"}, {"start": 20.0, "text": "how to integrate MusicKit into an app."}, {"start": 24.0, "text": "MusicKit is a Swift framework for Apple platforms"}, {"start": 28.0, "text": "that offers a set of APIs"}, {"start": 30.0, "text": "for your apps to access and play music."}, {"start": 35.0, "text": "Designed with Swift concurrency and SwiftUI in mind,"}, {"start": 39.0, "text": "MusicKit streamlines integration with Apple Music."}, {"start": 43.0, "text": "You can build rich, music enhanced experiences,"}, {"start": 47.0, "text": "so people can browse and play the Apple Music catalog"}, {"start": 51.0, "text": "and a person's media library"}, {"start": 54.0, "text": "straight from an app."}, {"start": 56.0, "text": "Alan and I will cover many key MusicKit concepts today"}, {"start": 60.0, "text": "while enhancing the workout app he and I made."}, {"start": 63.0, "text": "First, I'll explain how to configure Xcode and handle music access."}, {"start": 70.0, "text": "Then, I will cover what a MusicKit music item is"}, {"start": 74.0, "text": "and how I can select one."}, {"start": 78.0, "text": "Alan will build on my music selection work"}, {"start": 80.0, "text": "and prepare selected songs for playback."}, {"start": 85.0, "text": "And lastly, Alan will dive into music catalog requests"}, {"start": 89.0, "text": "to further customize the app he and I made."}, {"start": 94.0, "text": "Now, I want to go through the flow of the workout app"}, {"start": 97.0, "text": "before diving into MusicKit integration."}, {"start": 102.0, "text": "To follow along, you can find the completed sample code"}, {"start": 105.0, "text": "at the developer documentation site."}, {"start": 109.0, "text": "I'll run the app and go through the current flow."}, {"start": 113.0, "text": "When I start a bike workout, a screen pops up with a stopwatch,"}, {"start": 117.0, "text": "along with a button to end my session."}, {"start": 123.0, "text": "This is a good start, but I'd like to pick music to play during my workouts,"}, {"start": 128.0, "text": "so I want to integrate MusicKit!"}, {"start": 131.0, "text": "Before I start to code, I need to configure some settings"}, {"start": 134.0, "text": "as part of my project setup."}, {"start": 137.0, "text": "One of those settings is registering for a developer token"}, {"start": 141.0, "text": "on the developer portal, which is needed to make MusicKit requests."}, {"start": 146.0, "text": "Once you register for a developer token,"}, {"start": 149.0, "text": "it's generated on your behalf automatically."}, {"start": 154.0, "text": "To enable automatic token generation,"}, {"start": 157.0, "text": "I'll navigate to the page"}, {"start": 159.0, "text": "where I register my App ID."}, {"start": 162.0, "text": "I need to make sure the MusicKit checkbox is checked in the App Services tab."}, {"start": 169.0, "text": "The tokens are associated with my developer account,"}, {"start": 172.0, "text": "so I'll want to verify that I'm logged into that same account in Xcode."}, {"start": 179.0, "text": "Now, I'll return back to the app!"}, {"start": 182.0, "text": "I'd like to pick music to play during my workout,"}, {"start": 185.0, "text": "but before I can pick music,"}, {"start": 187.0, "text": "I have to give permission for MusicKit to access my music content."}, {"start": 192.0, "text": "To request authorization,"}, {"start": 195.0, "text": "I will use MusicKit's MusicAuthorization request method,"}, {"start": 200.0, "text": "which is an asynchronous method"}, {"start": 202.0, "text": "that returns whether the app's music access is approved."}, {"start": 207.0, "text": "MusicKit will then prompt the person with a permissions alert."}, {"start": 213.0, "text": "I have the ability to configure the description of this alert"}, {"start": 218.0, "text": "with more context for how my app will use the access,"}, {"start": 221.0, "text": "which I can do in my project settings."}, {"start": 225.0, "text": "To provide a reason to access music content,"}, {"start": 229.0, "text": "I'll navigate to the Signing & Capabilities tab of my Xcode project,"}, {"start": 234.0, "text": "and add the Media Library capability."}, {"start": 239.0, "text": "In the text box, I can describe how my app intends to use"}, {"start": 243.0, "text": "the person's music library."}, {"start": 246.0, "text": "This description will appear at the bottom of the permissions alert"}, {"start": 250.0, "text": "when you request authorization."}, {"start": 253.0, "text": "If the person isn't subscribed but wants to listen to content"}, {"start": 257.0, "text": "in the Apple Music catalog,"}, {"start": 259.0, "text": "I want to give them a way to subscribe."}, {"start": 262.0, "text": "An Apple Music subscription is not required to use MusicKit,"}, {"start": 267.0, "text": "but the app will only be able to access purchased or synced music without one."}, {"start": 273.0, "text": "If there isn't an active subscription,"}, {"start": 275.0, "text": "I can use a MusicKit subscription offer view modifier"}, {"start": 281.0, "text": "to give an opportunity to subscribe to Apple Music, without leaving my app."}, {"start": 286.0, "text": "The .musicSubscriptionOffer is a view modifier"}, {"start": 290.0, "text": "that accepts an isPresented binding parameter,"}, {"start": 293.0, "text": "which changes in this view when the button is tapped."}, {"start": 300.0, "text": "When presented, the subscription offer UI gives people steps"}, {"start": 304.0, "text": "to quickly sign up for an Apple Music subscription."}, {"start": 309.0, "text": "As a developer,"}, {"start": 310.0, "text": "you have the potential to earn commissions"}, {"start": 313.0, "text": "when someone subscribes to Apple Music through your app,"}, {"start": 317.0, "text": "as part of the Apple Services Performance Partner Program."}, {"start": 322.0, "text": "You can specify your information for this program"}, {"start": 325.0, "text": "in a MusicSubscriptionOffer.Options structure,"}, {"start": 330.0, "text": "and pass it into the view modifier."}, {"start": 333.0, "text": "The options struct also allows you to set a message identifier,"}, {"start": 338.0, "text": "which changes what UI is presented."}, {"start": 341.0, "text": "Since my ultimate goal is to play music, I'll set the messageIdentifier"}, {"start": 346.0, "text": "for my options as .playMusic."}, {"start": 351.0, "text": "You can customize the message identifier"}, {"start": 354.0, "text": "to have different UI treatments for your use case."}, {"start": 358.0, "text": "In the main view, I need to declare a @State property"}, {"start": 362.0, "text": "representing the subscription status."}, {"start": 365.0, "text": "I only want to have the subscription button if the person isn't subscribed,"}, {"start": 370.0, "text": "and has the potential to become subscribed."}, {"start": 374.0, "text": "To update the subscription status,"}, {"start": 377.0, "text": "I can add a .task that runs when authorization is granted."}, {"start": 381.0, "text": "In here, I'll grab the current value, as well as listen for any updates"}, {"start": 387.0, "text": "that might occur and set the value accordingly."}, {"start": 396.0, "text": "Now that I'm authorized and subscribed, I can use my subscription"}, {"start": 400.0, "text": "to play music from the Apple Music catalog"}, {"start": 403.0, "text": "during my workout!"}, {"start": 405.0, "text": "First, I need to pick a song to play."}, {"start": 408.0, "text": "Specifically, I am going to pick a MusicKit song object."}, {"start": 414.0, "text": "MusicItems are the building blocks for using MusicKit APIs,"}, {"start": 418.0, "text": "so I'll begin with explaining those."}, {"start": 421.0, "text": "Then, I'll explain how to pick music items using the music picker."}, {"start": 427.0, "text": "I'll dive into music items first."}, {"start": 430.0, "text": "An Album music item, for example, is a value type"}, {"start": 434.0, "text": "in MusicKit's model layer."}, {"start": 438.0, "text": "Each music item has Attributes, which are simple built-in properties."}, {"start": 444.0, "text": "An Album object, for example, has attributes that describe"}, {"start": 449.0, "text": "the title of the album,"}, {"start": 451.0, "text": "or what the album's contentRating is."}, {"start": 455.0, "text": "Music items also have Relationships which describe related content,"}, {"start": 461.0, "text": "like an Album's tracks, another MusicKit music item type."}, {"start": 467.0, "text": "Associations describe a type's related content as well,"}, {"start": 471.0, "text": "but associations generally have weaker ties to the type than a relationship has."}, {"start": 478.0, "text": "One Album association is the otherVersions,"}, {"start": 482.0, "text": "which is a collection of other albums."}, {"start": 486.0, "text": "So far I've focused on Album but there are many other MusicKit music item types,"}, {"start": 492.0, "text": "like Genres, Stations, and Playlists."}, {"start": 497.0, "text": "Now that I've covered what music items are,"}, {"start": 500.0, "text": "it's time to start using them!"}, {"start": 502.0, "text": "To pick music to listen to for my workout, I can utilize the music picker,"}, {"start": 508.0, "text": "which surfaces both the Apple Music catalog and the music library"}, {"start": 514.0, "text": "in a single, unified interface."}, {"start": 517.0, "text": "The music picker leverages many kinds of MusicKit requests in one place,"}, {"start": 522.0, "text": "allowing for several ways to discover music someone may want to pick."}, {"start": 528.0, "text": "To pick a MusicKit song,"}, {"start": 530.0, "text": "I need to add the .musicPicker SwiftUI view modifier!"}, {"start": 535.0, "text": "I have a base button already, but I need to add some state variables,"}, {"start": 540.0, "text": "starting with a toggle for if the picker should be shown."}, {"start": 544.0, "text": "Next, the selected song property represents an initial song selection."}, {"start": 550.0, "text": "I don't have anything selected, so it can be nil here by default."}, {"start": 556.0, "text": "Now, I can add the modifier."}, {"start": 561.0, "text": "Now, I'm going to add my musicPickerButton button to my main view"}, {"start": 566.0, "text": "where I added the other buttons."}, {"start": 569.0, "text": "The music picker does not require a subscription,"}, {"start": 572.0, "text": "which is why I have it regardless of the subscription check."}, {"start": 576.0, "text": "If there is no subscription, the picker will only show music items"}, {"start": 580.0, "text": "from the person's library, rather than both the library and catalog."}, {"start": 586.0, "text": "I'll now build and run this!"}, {"start": 589.0, "text": "I really like Olivia Dean, so I'll pick my current favorite Olivia Dean song."}, {"start": 595.0, "text": "To do so, I'm going to tap the search bar, and search for the song."}, {"start": 600.0, "text": "Once I find the result I want, I can tap the plus button"}, {"start": 604.0, "text": "on the right of the song information,"}, {"start": 606.0, "text": "and dismiss the picker."}, {"start": 609.0, "text": "Great, I have \"Lady Lady\" selected for my bike ride,"}, {"start": 612.0, "text": "but for a 30 minute workout, I want to listen to more than one song."}, {"start": 618.0, "text": "I need to allow for multi-selection in the picker"}, {"start": 622.0, "text": "by changing the selection object to an array."}, {"start": 626.0, "text": "I'll pick 3 songs to start,"}, {"start": 629.0, "text": "but I can also select entire albums or playlists"}, {"start": 633.0, "text": "by going to their detail pages and pressing the plus button at the top."}, {"start": 638.0, "text": "Okay, I've chosen my songs and can now dismiss the picker."}, {"start": 644.0, "text": "Now that I've selected music, I want to play it."}, {"start": 648.0, "text": "My teammate, Alan, will take it from here"}, {"start": 650.0, "text": "to go over adding playback to the workout app."}, {"start": 654.0, "text": "Thanks, Cathy."}, {"start": 656.0, "text": "Time to play some music, using MusicKit's MusicPlayers!"}, {"start": 660.0, "text": "MusicKit offers two different players, SystemMusicPlayer"}, {"start": 664.0, "text": "and ApplicationMusicPlayer."}, {"start": 667.0, "text": "Both players are subclasses of MusicPlayer."}, {"start": 671.0, "text": "SystemMusicPlayer controls the system Music app,"}, {"start": 675.0, "text": "while ApplicationMusicPlayer plays from your app."}, {"start": 680.0, "text": "With SystemMusicPlayer, you may only set the queue."}, {"start": 684.0, "text": "You can't see what is in the queue except for the currently playing item."}, {"start": 689.0, "text": "Meanwhile, you have full read and write access"}, {"start": 693.0, "text": "to the ApplicationMusicPlayer's queue."}, {"start": 697.0, "text": "Both music players let you set whether a queue will show up"}, {"start": 702.0, "text": "in the Music app's Recently Played"}, {"start": 705.0, "text": "and both allow you to set playback state,"}, {"start": 708.0, "text": "such as the Repeat and Shuffle mode."}, {"start": 713.0, "text": "Finally, because SystemMusicPlayer controls the system's Music app,"}, {"start": 718.0, "text": "it will continue playing even when your app is backgrounded or quits."}, {"start": 724.0, "text": "For the same backgrounding behavior using ApplicationMusicPlayer,"}, {"start": 729.0, "text": "enable the Audio Background Mode capability"}, {"start": 732.0, "text": "in the Xcode project settings."}, {"start": 735.0, "text": "A queue consists of a collection of playable music items,"}, {"start": 740.0, "text": "such as songs."}, {"start": 742.0, "text": "A queue is set on a MusicPlayer."}, {"start": 745.0, "text": "The MusicPlayer's repeat or shuffle behaviour is configurable by its state."}, {"start": 753.0, "text": "To start playing, call play() on the MusicPlayer."}, {"start": 758.0, "text": "To stop playing, call pause()."}, {"start": 763.0, "text": "First, the MusicPlayer loads the queue."}, {"start": 769.0, "text": "Then, the MusicPlayer has to load the audio assets"}, {"start": 773.0, "text": "before the player can output music!"}, {"start": 777.0, "text": "This may take a bit of time."}, {"start": 782.0, "text": "If you know ahead of time what to play, buffer the MusicPlayer"}, {"start": 787.0, "text": "using prepareToPlay()."}, {"start": 789.0, "text": "This reduces the amount of time needed to output music when you call play()."}, {"start": 796.0, "text": "A queue can be created from any playable music item, such as songs"}, {"start": 801.0, "text": "or container types, such as an album or playlist."}, {"start": 808.0, "text": "Using the special queue initializers for container types"}, {"start": 812.0, "text": "allows the music player to lazily load the container's items,"}, {"start": 817.0, "text": "further reducing the load time!"}, {"start": 820.0, "text": "When music is played using MusicKit, it will generally appear"}, {"start": 824.0, "text": "in the person's listening history in the Music app."}, {"start": 827.0, "text": "affectsListeningHistory is an instance property"}, {"start": 830.0, "text": "that determines whether the queue will show"}, {"start": 833.0, "text": "in the person's Recently Played shelf in the Music app."}, {"start": 837.0, "text": "It defaults to \"true\" but respects the Use Listening History setting"}, {"start": 842.0, "text": "for the Music app."}, {"start": 844.0, "text": "To observe and control the player, both MusicPlayers have observable"}, {"start": 850.0, "text": "properties for their playback state and queue."}, {"start": 854.0, "text": "ApplicationMusicPlayer has a queue where you have full control."}, {"start": 859.0, "text": "These are observable classes that you can use directly in your SwiftUI view ."}, {"start": 865.0, "text": "To learn more about observation in Swift, check out the Discover Observation"}, {"start": 869.0, "text": "in SwiftUI session from WWDC 2023."}, {"start": 874.0, "text": "In the workout app, I'd like to put the artwork"}, {"start": 877.0, "text": "front and center during a workout."}, {"start": 880.0, "text": "I'll also show the title and subtitle of the current song, and a set of controls."}, {"start": 889.0, "text": "To get the currently playing song, I'll reference the player's queue."}, {"start": 895.0, "text": "Then, if the currently playing song in the queue has an artwork,"}, {"start": 899.0, "text": "I'll use MusicKit's ArtworkImage SwiftUI view to display the artwork."}, {"start": 906.0, "text": "To show the song info, I'll use the title and subtitle"}, {"start": 911.0, "text": "of the currently playing entry."}, {"start": 914.0, "text": "To control play/pause, I'll add a button."}, {"start": 918.0, "text": "I'll read the state of ApplicationMusicPlayer"}, {"start": 922.0, "text": "and derive whether it's currently playing by checking playbackStatus."}, {"start": 927.0, "text": "The button calls \"pause\" when the player is currently playing"}, {"start": 931.0, "text": "and \"play\" otherwise."}, {"start": 938.0, "text": "Finally, the Back button calls skipToPreviousEntry"}, {"start": 942.0, "text": "to go to the previous song"}, {"start": 944.0, "text": "and skipToNextEntry in the Next button."}, {"start": 951.0, "text": "In the music picker, I can tap on the plus button at the top of my running playlist"}, {"start": 956.0, "text": "to choose all the songs in this playlist."}, {"start": 959.0, "text": "Then, I'll tap on Done."}, {"start": 961.0, "text": "The first song is playing now,"}, {"start": 963.0, "text": "and the ArtworkImage I just put reflects the currently playing song!"}, {"start": 967.0, "text": "I'll tap on Pause, and the player pauses."}, {"start": 971.0, "text": "Tap on Next, and the artwork now shows the next song!"}, {"start": 975.0, "text": "I'd like to make it more convenient for those using my app"}, {"start": 978.0, "text": "to get their workout going"}, {"start": 980.0, "text": "by suggesting some songs that they can tap on in the workout view"}, {"start": 983.0, "text": "to quickly start listening!"}, {"start": 986.0, "text": "Catalog requests allow your app to query Apple Music and provide music content"}, {"start": 991.0, "text": "independent of the person's library, such as curated content for your app."}, {"start": 997.0, "text": "In MusicKit, structured music catalog requests"}, {"start": 1000.0, "text": "allow you to fetch content from Apple Music API."}, {"start": 1005.0, "text": "MusicKit offers several structured requests,"}, {"start": 1008.0, "text": "such as getting items based on a specific filter, searching for music,"}, {"start": 1013.0, "text": "and other Apple Music curated and personalized content!"}, {"start": 1017.0, "text": "Explore the MusicKit documentation for the full list and how to use them."}, {"start": 1023.0, "text": "MusicCatalogResourceRequest"}, {"start": 1025.0, "text": "is a structured catalog request"}, {"start": 1027.0, "text": "for a specific resource."}, {"start": 1029.0, "text": "In this example, we'll make a request for songs."}, {"start": 1033.0, "text": "A request contains some configurations, such as options,"}, {"start": 1037.0, "text": "if you want to set the behavior of the request."}, {"start": 1040.0, "text": "I'll talk more about options in a moment."}, {"start": 1044.0, "text": "You can set properties on the request that specifies relationships"}, {"start": 1049.0, "text": "and associations you also want to fulfill"}, {"start": 1051.0, "text": "as part of this songs request."}, {"start": 1054.0, "text": "For example, you may also want the Artists relationship of the song."}, {"start": 1060.0, "text": "And, you can set a limit on the number of items to return in the response."}, {"start": 1067.0, "text": "When you call the asynchronous response() method, MusicKit fulfills the request."}, {"start": 1074.0, "text": "The method returns a MusicCatalogResourceResponse"}, {"start": 1078.0, "text": "which contains the results of the request"}, {"start": 1081.0, "text": "as a strongly-typed MusicItemCollection."}, {"start": 1085.0, "text": "MusicItemCollection is a MusicKit type, containing a collection of music items."}, {"start": 1090.0, "text": "In this example, it contains Songs."}, {"start": 1095.0, "text": "MusicItemCollection supports pagination,"}, {"start": 1098.0, "text": "so if your request produced too many results,"}, {"start": 1101.0, "text": "hasNextBatch will be \"true\" and you can get the next page"}, {"start": 1104.0, "text": "using the asynchronous nextBatch() method."}, {"start": 1108.0, "text": "When making resource requests,"}, {"start": 1110.0, "text": "resource availability depends on the account's settings"}, {"start": 1113.0, "text": "and storefront or region."}, {"start": 1115.0, "text": "For example, a resource you request in one region"}, {"start": 1119.0, "text": "may have an equivalent resource with a different ID in another region."}, {"start": 1124.0, "text": "Additionally, a resource for explicit content"}, {"start": 1128.0, "text": "may have an equivalent clean resource"}, {"start": 1130.0, "text": "when the account does not allow explicit content."}, {"start": 1138.0, "text": "I'll make a fetchSongs method that has an input of a collection of song IDs,"}, {"start": 1143.0, "text": "where the first ID is treated as a featured song."}, {"start": 1147.0, "text": "The method uses a MusicCatalogResourceRequest"}, {"start": 1151.0, "text": "for songs that match the IDs"}, {"start": 1152.0, "text": "in the songIDs argument."}, {"start": 1156.0, "text": "I'll also add the findEquivalents option flag"}, {"start": 1159.0, "text": "to enable the resource equivalency behavior I just talked about."}, {"start": 1164.0, "text": "Then, I'll call the response() method to fetch the content."}, {"start": 1170.0, "text": "To capture the featured song, I'll use the item(for:) method"}, {"start": 1174.0, "text": "using the first ID in the input collection of IDs."}, {"start": 1179.0, "text": "The catalog request is not guaranteed to return everything that was requested,"}, {"start": 1184.0, "text": "such as if a resource is unavailable."}, {"start": 1188.0, "text": "Finally, I'll get the other songs in order."}, {"start": 1193.0, "text": "Now I'm ready for my workout!"}, {"start": 1195.0, "text": "I'll go back to my app, start another workout,"}, {"start": 1198.0, "text": "and now I have a shelf of some songs that you can quickly pick"}, {"start": 1202.0, "text": "when you want to get your workout going now!"}, {"start": 1205.0, "text": "Tapping on one of these artworks will immediately start playing it!"}, {"start": 1211.0, "text": "That's all you need to know about integrating MusicKit into your app!"}, {"start": 1216.0, "text": "As Cathy talked about, adopt the music picker view modifier"}, {"start": 1220.0, "text": "to provide a unified and familiar music picking experience in your app!"}, {"start": 1226.0, "text": "The Apple Music catalog contains a wealth of content that your app can play."}, {"start": 1230.0, "text": "For example, add some background music to enrich your app experience!"}, {"start": 1235.0, "text": "And, check out other MusicKit APIs, such as requests to browse and modify"}, {"start": 1241.0, "text": "library content covered in \"Explore more content with MusicKit\","}, {"start": 1245.0, "text": "from WWDC2022."}, {"start": 1248.0, "text": "If you're interested in integrating on Android or the web,"}, {"start": 1252.0, "text": "check out \"Meet Apple Music API and MusicKit\"."}, {"start": 1257.0, "text": "Thank you for watching."}]}