스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
MusicKit으로 더 많은 콘텐츠 살펴보기
MusicKit을 사용하여 앱을 개선하고 개인화하는 방법을 알아보세요. MusicKit 프레임워크의 최신 추가 사항을 안내하고 요청, 메타데이터 등을 통해 음악 콘텐츠를 앱에 제공하는 방법에 대해 알아보겠습니다.
리소스
관련 비디오
WWDC22
WWDC21
-
다운로드
♪ ♪
WWDC에 오신 것을 환영합니다, 여러분 저는 David고요 MusicKit에서 더 많은 컨텐츠를 즐길 수 있는 방법을 알려드리겠습니다 MusicKit 프레임워크는 2021년 시작되어 Swift 내 음악에 액세스해 재생하는 API를 제공합니다 이걸 통해 앱을 Apple Music에 통합하여 Apple Music 카탈로그 전체에 액세스할 수 있죠 오늘 MusicKit의 몇 가지 주요 개선 사항을 알려드리려고 합니다 우선, 카탈로그에서 추가로 얻을 수 있는 걸 말씀드리죠 새로운 음악 항목 유형과 요청 메타데이터가 추가되었습니다
그 다음 각 유저에게 맞춤형 경험을 선사하기 위해 개인화 콘텐츠를 불러오는 방법에 대해 말씀드리겠습니다 다음으로 Apple Music 카탈로그를 넘어가 볼겁니다 올해 MusickKit은 차원이 달라졌습니다 유저 라이브러리의 음악을 포함해서요 플레이리스트 만들고 편집하기 항목 추가하기 등 라이브러리와 활발하게 상호 작용할 수 있는 방법을 마지막에 이야기해 보죠 카탈로그 콘텐츠 추가 기능을 먼저 살펴 보겠습니다 MusicKit의 등장을 통해 Songs, Albums, Playlists 등의 핵심 유형을 포함해 새로운 음악 모델 계층이 도입됐습니다 올해 두 가지 유형을 추가할 예정인데요 Curator와 Radio Show를 통해 새로운 음악을 더욱 쉽게 발견할 수 있습니다
또한 이제 MusicKit에서는 카탈로그를 통한 검색 UI 구축 최고 인기곡, 앨범 등 인기 차트 액세스가 가능합니다 Dolby Atmos와 함께한 Spatial Audio 등 고품질 음향 메타데이터 같은 새로운 속성도 가져올 수 있죠 획기적인 음악 발견이 가능한 큐레이터와 라디오쇼 이 둘 부터 살펴볼까요? Curator의 사례로 Nike를 살펴 보죠 다른 큐레이터로는 Shazam, Beats 등이 있습니다 이 큐레이터를 통해 플레이리스트 확인이 가능합니다 원하는 플레이리스트를 빠르게 찾아볼 수 있습니다 새로운 노래를 찾거나 최애곡을 다시 들을 수 있죠 기술적인 부분을 좀더 자세히 알아봅시다 Curator에는 다양한 속성이 있는데요 이 Curator 유형의 주요 속성으로는 곡명, url, 아트워크, 종류가 있습니다 kind 프로퍼티는 열거형으로 '편집형'이나 '외부형'입니다 Apple 혹은 제3의 큐레이터일 수 있다는 거죠 Curator의 관계에는 플레이리스트도 있는데요 큐레이터가 만든 플레이리스트를 보여주고 앞서 봤던 발견 기능을 제공합니다
다음은 Radio Show 유형입니다 이 유형은 Apple Music에서 서비스되는 라디오 방송으로 노련한 전문가들을 통해 새로운 음악을 접할 수 있는 또다른 방법입니다 Curator와 같이 라디오 쇼 소개 음악을 찾을 수 있는 플레이리스트 관계가 있습니다 이 두 가지 유형이 플레이리스트와 관계를 가지듯 Playlist 유형에서도 두 가지 새로운 관계가 드러납니다 앞선 두 가지 유형이 역논리로 사용되는 건데요 어떤 실체가 플레이리스트를 불렀는지 파악이 가능하죠
여러 유형에서의 콘텐츠 카탈로그 검색도 가능합니다 큐레이터와 라디오 쇼 같은 새로운 항목 유형도 지원합니다 리스트가 점점 더 늘어나면서 좋은 UI를 구축하는 것이 점점 더 어려워졌는데요 그래서 최상위 검색 결과와 검색 제안 기능을 추가해 UI 구축이 더욱 쉬워졌습니다 이러한 기능 향상으로 얼마나 유용한지 확인해 봅시다 콘텐츠 검색어를 입력할 때 음악 관련 자동 완성 기능이 강력하길 원하실 겁니다 여기서 '검색 제안'이 역할을 하죠 찾으려는 용어를 제시하면서요 여기서 한 단계 나아가 상위 검색 결과도 나타납니다 사람들이 검색하는 것들을 빠르게 확인해 볼 수 있죠 원하는 검색 결과를 위해 항목 유형은 상관 없지만 얼마나 관련이 있는지가 중요하니까요 이게 '상위 검색 결과'의 강점입니다 이제 이걸 구현하는 방법을 살펴 볼까요? 상위 검색 결과부터 보죠 기존의 카탈로그 검색 요청문 만드는 방식인데요 검색어와 원하는 항목 유형을 요청하는 것으로 나타나 있죠 응답 상태엔 요청 유형에 따라 분류된 컬렉션이 포함됩니다 유형에 맞는 결과에 따라 다양한 리스트가 나타나는 거죠 이것도 좋지만 우리는 요청 유형 상관 없이 가장 관련성이 높은 리스트 하나만 원합니다 이 정보 요청을 위해서는 한 줄만 추가하면 되는데요 여기서 includeTopResults 프로퍼티를 참으로 설정합니다 그럼 새 프로퍼티가 응답 상태에 채워지죠 새 프로퍼티의 명칭은 topResults입니다 요청 유형 항목을 포함하죠 print 명령문 출력 시 다음과 같이 나타납니다 상위 검색 결과에 단일 컬렉션 내에 있는 곡, 아티스트, 앨범이 나타나고 관련도순으로 나타납니다 이제 '검색 제안'을 통해 원하는 결과를 더욱 빠르게 얻을 수 있는 방법을 살펴 봅시다 하나의 문자열만으로도 검색 제안 요청이 가능합니다 응답 상태를 호출하자마자 검색 제안 응답이 나타납니다 이 응답 상태에는 Suggestion 배열이 포함되는데요 각 제안에는 검색어 뿐 아니라 UI에 적합한 표제어가 포함됩니다 검색 제안 결과를 선택할 때 검색어로 검색 요청을 수행하여 일치하는 결과를 가져올 수 있고요
카탈로그 차트는 최고 인기곡의 최신 정보를 제공합니다 MusicKit에서는 다양한 차트 유형을 통해 트렌드를 알려주죠 제공 차트 유형으로는 최다 재생 곡과 일치하는 인기곡, 인기 앨범과 같은 인기 차트와 도시별 차트 일간 top 100이 있습니다 요청 차트를 특정 장르에 따라 필터링할 수도 있습니다 코딩을 통한 차트 불러오기도 굉장히 간단합니다 카탈로그 차트 요청에서는 카탈로그 검색 요청에서 이미 사용된 패턴을 따르는데요 먼저, 차트 요청을 초기화하세요 그 다음 원하는 차트 종류를 명시하면 됩니다 기본적으로 여기서는 최다 재생 콘텐츠가 나타나지만 일간 해외 종합, 도시별 인기 차트 포함도 가능합니다 마지막으로 차트에 포함됐으면 하는 유형을 명시하세요 다 됐습니다 응답 상태에서 첫 번째 플레이리스트 차트를 보시면 일간 해외 종합 인기 차트가 나타납니다 항목은 Top 100: Global Top 100: USA 등이네요 이전에는 MusicDataRequest 구조를 사용해야 했지만 새로운 MusicKit에서는 그럴 필요가 없습니다 항목 컬렉션에 대한 페이징이 지원되기 때문이죠 2021년 우리는 획기적인 명료한 다차원 사운드로 획기적인 음향 경험을 도입했습니다 바로, Dolby Atmos 지원 Spatial Audio인데요 이미 수천 곡에 대한 이 몰입형 경험이 가능합니다 이제 이 데이터를 확인하실 수 있고요 MusicKit에서는 음향 변형을 통해 이용 가능 리소스를 알려주고 다른 이에게 정보를 전달합니다 음향 변형 사례로는 이전에 언급했던 Spatial/Lossless Audio 등이 있습니다
새로운 불리언 프로퍼티도 노출하고 있는데요 최고 음질 마스터가 지원되는 Apple Digital Master입니다 이 메타데이터는 항목 수준에서 노출되어 상세 뷰에 완벽한 음향 변형입니다 이런 UI를 만드실 수 있죠 한 앨범의 상세 뷰입니다 여기 이전에 언급했던 음향 변형을 기반으로 적절한 배지가 나타나 있네요 음질을 예상할 수 있죠 이 예시에 나온 앨범은 공간 음향과 무손실 음향으로 이용할 수 있네요 그럼 코딩을 한 번 해볼까요? 음향 변형 로딩은 다른 확장 속성 로딩과 비슷합니다 기존 앨범이나 곡을 선택하세요 여기선 앨범을 선택했네요 with 메소드로 audioVariants 확장 속성을 로딩하세요 detailedAlbum에는 audioVariants 프로퍼티가 채워졌습니다 음향 변형 프로퍼티가 이렇게 나타는데요 요소가 audioVariants인 배열이 나타났네요 이전에 살펴 봤듯이 이 값들을 통해 여러분의 UI에 특정 요소에 대해 이용 가능한 리소스를 나타낼 수 있습니다 좋습니다 하지만 최상단이나 디테일 뷰에 음향 배지를 보여줘야겠죠 이게 바로 여기서 한 단계 더 나아가 활성 음향 변형을 노출하는 이유입니다 활성 음향 변형 액세스를 통해 현재 재생 항목의 음질을 시각적으로 나타낼 수 있습니다 여기서는 Dolby Atmos라고 나타나네요 이 API에서는 사용자 설정과 네트워크 환경에 따라 올바른 음질을 자동 선택하기도 하는데요 플레이어의 활성 특질에 액세스하기 위해 우선 명령 객체에서 ApplicationMusicPlayer 클래스의 재생 상태에 액세스합니다 그 다음 재생 상태에서 직접 활성 audioVariant에 액세스하고 dolbyAtmos인지 확인하여 맞는 경우 UI에 추가합니다 재생 상태는 명령 객체이기 때문에 이 뷰에서는 현재 재생곡이 바뀔 때마다 자동으로 업데이트되어 뷰가 항상 최신 상태로 유지될 겁니다 카탈로그 추가 기능을 살펴 보았으니 다음으로 개인화 콘텐츠 가져오기로 넘어가 봅시다 개인화 콘텐츠는 한 구독자에 특정되는 데이터입니다 모든 앱 유저에게 독특한 맞춤형 경험을 제공하죠 보통 개인화 콘텐츠에는 인증과 유저 토큰이 필요하지만 MusicKit 프레임워크에서는 그렇지 않습니다 모두 자동화되어 있기 때문에 귀찮을 일이 없죠 개발자들을 위해 가져온 개인화 콘텐츠는 최근 재생 항목과 개인 추천곡에 대한 액세스입니다 최근 재생 콘텐츠는 음악 소비 경험 데이터에서 굉장히 중요한 부분입니다 유저가 즐기는 음악 항목에 빠르고 쉽게 액세스할 수 있죠 새로운 곡을 듣는 경우 나중에 돌아가서 재생 내역을 확인할 수 있습니다 앨범, 플레이리스트 스테이션 등 최근 재생 컨테이너를 가져오려면 요청이 필요합니다 플레이리스트나 앨범에서 곡을 재생할 경우 컨테이너 유형을 불러오게 되는데요 응답 상태에 최근 재생 음악 항목이 있는데요 제목, 부제, 아트워크에 편하게 액세스할 수 있습니다 노래나 스테이션 등 특정 유형의 최근 재생 항목도 가져올 수 있고요 여기서는 Song이라는 일반 매개 변수에 꺽쇠괄호를 하여 요청문을 만들 수 있습니다 그러면 응답 상태에는 재생한 곡들만 포함되죠 다음은 개인 추천곡입니다 개인 추천곡은 앱 경험을 보다 친밀하게 만드는데요 유저 라이브러리와 청취 목록을 기반으로 생성되기 때문입니다 추천곡은 테마에 따라 보기 좋게 정리되기 때문에 장르, 아티스트, 컬렉션에 따라 그룹화됩니다 개인 추천곡을 가져오려면 개인 추천곡 요청문을 만들면 되겠죠 응답 상태는 추천곡 컬렉션이 됩니다 첫 추천곡을 로깅해 보니 여기 있는 특정 요소는 "Made for You" 추천곡이네요 id, title, nextRefreshDate이 나타납니다 nextRefreshDate는 가장 최신 제안에 맞춰 추천이 언제 리프레시 되는지를 나타냅니다 playlists 프로퍼티에는 나만의 플레이리스트가 포함됩니다 추천곡의 또다른 예시를 살펴 볼까요? 여기서는 추천곡 응답 상태의 두 번째 요소를 출력하겠습니다 대체곡이 굉장히 많고 이 추천곡들에는 앨범과 플레이리스트 같이 다양한 유형이 섞여 있습니다 단일 항목 컬렉션으로 그룹화되는데요 관련도 순서로 카탈로그 검색 상위 노출 결과와 비슷합니다 이제 한 단게 더 나아가 유저 라이브러리 콘텐츠를 앱에 통합함으로써 훨씬 더 연관된 경험을 할 수 있는 방법에 대해 알아보도록 합시다 올해 MusicKit에서는 라이브러리 요청과 라이브러리 섹션별 요청을 통해 앱에서 라이브러리 항목을 가져올 수 있게 되었습니다 유저 라이브러리 콘텐츠를 검색할 수도 있고 라이브러리의 확장 속성과 관계를 구체적으로 로딩할 수도 있게 됐고요 기술적인 면을 살펴보기 전에 앱 향상을 위한 라이브러리 콘텐츠 사용법을 알아 봅시다 저는 실외 달리기 추적 피트니스 앱 Music Marathon 개발 작업을 하고 있습니다 MusicKit을 이 프로젝트에 결합하여 Apple Music 앱과 번갈아 사용하는 대신 이 앱을 통해 직접적인 음악 재생이 가능해졌죠 새로운 운동을 시작하고 음악 콘텐츠를 둘러 봅시다
그럼 개인 추천곡 요청에서 불러온 플레이리스트가 나타나는데요 유저 취향 플레이리스트에 빠르게 액세스할 수 있죠 Library 탭에 가시면 텅 빈 화면이 나타납니다 개인 플레이리스트 전체를 볼 수 있다면 좋을텐데요 이 기능을 코드로 써 봅시다 이미 뷰에서 기본적인 것들을 처리하기 위해 UI는 설정됐고 이제 라이브러리의 플레이리스트를 로딩해 볼게요 먼저 라이브러리 요청을 하고
일반 매개 변수에 플레이리스트를 구체화해 라이브러리에서 가져오도록 해보겠습니다
이걸 지역 변수 "request"로 저장할게요
다음으로 이 요청을 가져와서 응답 함수를 호출하겠습니다
async throwing 메소드이므로 try await 키워드를 추가할게요 그리고 다시 한 번 응답 변수로 저장하겠습니다
그 다음 이 응답을 받기 위해 상태 객체를 업데이트할게요
이제 남은 건 리스트를 업데이트해서 UI에 플레이리스트를 표시하는 건데요 ForEach 메소드를 사용해 응답 내 항목을 반복하겠습니다
그리고 MusicItemCollection 구조에서 각 플레이리스트를 불러 올게요
플레이리스트가 있으니 PlaylistCell에 전달할게요
다시 실행하면 어떻게 될까요?
앱으로 돌아가서 살펴보면 이제 라이브러리에 개인 플레이리스트가 보이네요 Apple Music 카탈로그와 개인 라이브러리의 개인 추천곡을 모두 들을 수 있게 됐습니다 라이브러리 콘텐츠 액세스가 쉽다는 걸 알았으니 또 뭘 할 수 있는지 한 번 볼까요? 음악 라이브러리 요청은 유저 라이브러리 항목을 가져올 수 있는 강력한 API입니다 다른 요청과는 다르게 MusicLibraryRequest 구조는 iOS에서 네트워크 데이터를 실제로 로딩하지 않습니다 대신 장치에 저장된 유저 라이브러리 복사본의 항목들을 로딩하죠 이 요청의 기본 원리는 원하는 음악 항목 유형을 구체화하는 것입니다 이 항목 유형은 MusicLibraryRequest의 일반 매개 변수를 통해 전달되죠 필요에 맞게 호출을 미세하게 조정하기 위해 요청문에 여러 필터를 적용하고 옵션을 분류할 수 있습니다 이 요청으로 이미 다운로드된 콘텐츠를 가져와서 완전한 오프라인 경험을 할 수도 있습니다 우리가 앞서 Music Marathon 앱에서 사용한 간단한 요청이지만 이번에는 앨범을 요청해 볼게요 앨범 유형은 일반 매개 변수를 통해 구체화됩니다 요청을 수행하기 위해 응답 함수를 호출하세요 출력 결과를 보시면 MusicLibraryResponse 구조가 나타나죠 항목은 유저의 음악 라이브러리 내 발견되는 모든 앨범에 대한 MusicItemCollection 구조입니다 Album 모두 다양한 카탈로그 요청문과 동일하고 기능도 동일하다는 게 보이네요 하지만 이 예시에서는 앨범만 가져올 것이기 때문에 앨범이라는 특정 부분 집합만 필요합니다 MusicLibraryRequest가 라이브러리에서 어떤 항목을 가져올지 더 구체적으로 명시하는 이유입니다 이전에 사용했던 요청문에 필터를 더해 볼게요 여기선 isCompilation 프로퍼티가 참인 모든 앨범을 로딩하고자 합니다 filter 메소드를 호출하면 Xcode의 자동 완성 기능은 요청하는 항목 유형에 지원되는 특정 주요 경로만 제공합니다 이제 응답 상태에는 컴필레이션 앨범만 나타나네요 하지만 MusicLibraryRequest의 강점은 이게 끝이 아닙니다 다양한 필터를 연쇄로 사용하여 추가 필터마다 보다 정제된 요청이 가능하죠 특정 장르의 컴필레이션 음반을 모두 원할 때는 어떨까요? 이 요청문에 또다른 필터를 추가하면 됩니다 "Dance"라는 장르 인스턴스가 있네요 결과를 제한하기 위해 장르 관계에 따라 필터링되어 이 특정 장르만 포함한 컴필레이션만 나오게 됩니다 응답 상태엔 댄스 컴필레이션 앨범만 나와 있네요 그럼 이미 다운로드된 댄스 컴필레이션만 원한다면요? 요청문에 includeOnlyDownloadedContent 인스턴스 프로퍼티를 참으로 설정하면 됩니다 다 됐네요 응답 상태에 동일한 MusicLibraryResponse 구조가 나타나지만 이 항목에는 다운로드된 요소만 나타납니다 보시다시피 이 요청은 매우 강력하고 기존의 MusicDataRequest 구조로는 불가능했던 것을 가능케 합니다 유저 라이브러리 데이터를 가져올 방법은 훨씬 많습니다 라이브러리 섹션별 요청을 살펴 보죠 섹션별 요청을 통해 섹션별 그룹 항목을 가져올 수 있고 그렇기 때문에 두 개의 일반 매개 변수를 취합니다 섹션 유형 인수와 항목 유형 인수죠 라이브러리 섹션별 요청은 일반 라이브러리 섹션과 동일한 기능을 지원하기 때문에 다얗안 필터와 분류 메소드를 섹션이나 항목에 적용할 수 있습니다 라이브러리 섹션별 요청을 통해 장르별로 구분된 앨범을 모두 가져와 보겠습니다 이 응답 상태엔 각 요소가 첫 일반 매개 변수가 Genre인 "sections"라는 프로퍼티가 나타납니다 각 Genre에서는 속성이 노출될 뿐 아니라 앨범 컬렉션을 포함합니다 items 프로퍼티로 접근 가능하죠 해당 항목들은 두 번재 일반 인수와 일치합니다 표시된 부분은 Alternative 장르인 앨범만 보여줍니다 아까 말씀드렸듯이 필터링과 분류 기능은 섹션 요청에서도 이용할 수 있습니다 동일한 앨범을 장르별로 섹션화하고 그 앨범들을 아티스트명에 따라 분류해 봅시다 sort 메소드를 추가했습니다 Albums에서 artistName이라는 keyPath 프로퍼티를 명시하고 오름차순으로 정렬함으로써 응답 상태를 분류해 보았습니다 분류 작업이 섹션이 아닌 항목에 적용되었기 떄문에 sortItems 메소드라는 것을 알 수가 있습니다 섹션을 지정하고 싶었다면 filterSections과 sortSection 메소드를 이용할 수 있겠죠 다른 응답 상태를 한 번 살펴볼까요?
앨범 순서가 타이틀이 아닌 아티스트명의 알파벳순으로 나열된 것이 확인되는데요 두 요청 모두 굉장히 강력하지만 유저 라이브러리 검색 결과를 추가함으로써 음악 검색 UI를 보완하고 싶으실 겁니다 그래서 우리는 카탈로그 검색과 거의 동일하게 실행되는 새로운 구조의 요청을 만들었습니다 카탈로그 결과 로딩 대신 라이브러리 관련 항목을 찾죠 카탈로그 요청과 같이 라이브러리 검색 결과에는 검색어와 유형 배열만이 필요합니다 유저 라이브러리 항목을 불러오는 다양한 방법을 봤으니 확장 속성과 관계를 로딩해 볼까요? 아시겠지만 MusicKit 초기 배포판에서는 with 메소드를 써 Apple Music API의 프로퍼티를 직접 로딩했습니다 올해 Current With 메소드로 확장해 선호 소스 매개 변수도 취하게 됐습니다 이는 Apple Music 카탈로그와 사용자 라이브러리에서 이용 가능한 확장 속성과 관계 데이터를 어디서 로딩할지 보여줍니다 카탈로그나 라이브러리에 있는 프로퍼티 모두 선호 소스 상관 없이 하나도 빠짐 없이 가져오게 될 겁니다 또, 이 기능은 초기 항목의 출처가 카탈로그 요청이든 라이브러리 요청이든 상관없이 언제든 사용할 수 있습니다 모두 작동 가능하죠
다음은 음악 항목 관계를 받아 오는 방법인데요 앨범 트랙을 로딩하여 출력물이 나타나면 이렇게 앨범의 모든 트랙을 확인할 수 있습니다 그러나 preferredSource 프로퍼티를 추가하면 라이브러리의 관계를 가져오고 싶다고 명시할 수 있죠 이렇게 라이브러리 내 앨범의 트랙만 나오게 됩니다 유저 라이브러리 항목을 가져올 수 있는 여러 방법으로 유저들은 MusicKit를 통해 직접 라이브러리와 상호작용할 수 있습니다 이제 예시로 보여드렸던 피트니스 앱으로 돌아가 라이브러리가 제공하는 기능을 확인해 봅시다 저는 운동할 때 개인 추천곡들을 확인하고 싶은데요
트랙을 쭉 살펴보다 보면 Workout 플레이리스트에 딱인 트랙을 발견할 때가 있습니다 이 셀들 중 하나를 꾹 누르면 바로 가기 메뉴가 나타납니다 그럼 이 노래를 플레이리스트에 추가할 수 있죠 이 버튼을 눌러 보면 모든 플레이리스트가 등장합니다 그럼 코딩 작업을 해볼까요? 이미 Add to Playlist 셀에 선정 항목을 보냈으니 공통 인스턴스를 통해 MusicLibrary 클래스를 액세스하죠
add 메소드를 호출하시고 선정 트랙과 추가를 원하는 플레이리스트를 명시해 주세요
이 메소드는 async throwing 함수이기 떄문에 여기서도 try await 키워드를 추가하겠습니다
마지막으로 피커 기능을 없애기 위해 isShowingPlaylistPicker 프로퍼티를 거짓이라고 설정합시다
재실행 해볼까요? 그리고 트랙 하나를 특정 플레이리스트에 추가한다면 이 항목이 추가될 것으로 예상되는데요 앱의 Library 탭으로 돌아가면 해당 곡이 Workout에 추가된 것이 보이실 겁니다 정말 간단한 작업이죠? 이제 라이브러리가 제공하는 다른 기능들도 살펴보겠습니다 라이브러리와의 상호 작용 방법으로는 라이브러리 내 콘텐츠 추가 플레이리스트 만들기 플레이리스트 메타데이터와 트랙 리스트 편집이 있는데요 첫 번째 상호 작용을 통해 Apple Music의 Library 탭에 특정 곡이나 앨범을 찾을 수 있습니다 Settings에서 Sync Library 기능을 켜면 모든 연결 장치에서 동기화가 가능합니다 앱에 이 기능을 직접 제공하여 두 앱 사이를 왔다갔다 할 필요가 없죠 즉, 제공 콘텐츠 참여를 유지할 수 있는 겁니다 또, 새로 도입된 라이브러리 요청을 통해 앱을 라이브러리에 통합함으로써 이런 결과들로부터 바로 혜택을 얻을 수 있습니다 유저들은 좋아하는 콘텐츠에 쉽게 액세스할 수 있게 되고요 이 강력한 서비스가 있음에도 새로운 음악 경험을 계속해서 만들고 싶습니다 그래서 플레이리스트 만들기와 편집 기능을 도입했고요 이제 유저들을 대표해 플레이리스트를 만들 수 있고 노래나 앨범 전체 항목을 유저 라이브러리 속 적합한 플레이리스트에 추가할 수도 있습니다 이 기능은 사람들이 좋아하는 콘텐츠를 그룹화하거나 앱 분위기를 설정할 수 있는 아주 좋은 기능이죠 또, 기존 플레이리스트에 콘텐츠를 추가하여 MusicKit에서 제공하는 다양한 음악 발견 툴이 사람들에게 직접적인 영향을 주게 할 수도 있습니다 만들었던 플레이리스트의 편집이 가능하기에 트랙 리스트나 메타데이터를 편집해 원하는 대로 만들 수 있죠 이렇게 앱 내에서 유저 라이브러리와 상호 작용할 수 있는 법을 알아보았는데요 마지막으로 올해 MusicKit의 업그레이드 내용을 정리해 보죠 새로운 유형, 프로퍼티 검색 확장기능을 기존 앱에 쉽게 결합하여 더 나은 경험을 제공하고
라이브러리 콘텐츠와 기능을 결합하여 새로운 기능을 경험할 수 있기에 사용자들이 본인의 경험을 통제할 수 있습니다
또 MusicKit를 통해 다른 앱의 향상이 가능합니다 피트니스 앱, 소셜 미디어 앱 지도 앱 등 음악 재생이나 공유를 통한 혜택을 느껴보세요 더 나아가 다른 관련 세션도 확인해 보시길 바랍니다 Apple 프레임워크를 최대한 활용할 수 있도록 Swift의 새로운 명령어를 학습하며 집중 탐구해 보세요 WWDC 21 MusicKit 세션에서 프레임워크 사용, 재생 초기화 구독 서비스 제공을 위해 앱을 설정하는 법을 알아보세요 Android나 웹을 Apple Music과 통합하고 싶다면 이 세션을 활용해 보시고요
이번 세션이 즐거우셨길 바라며 개발자 포럼에 통해 새로운 정보를 얻어가실 수 있기를 바랍니다 시청해 주셔서 감사합니다 WWDC 2022를 즐겨 보세요
-
-
4:20 - Existing catalog search request
// Loading catalog search top results var searchRequest = MusicCatalogSearchRequest( term: "Hello", types: [ Artist.self, Album.self, Song.self ] ) let searchResponse = try await searchRequest.response() print("\(searchResponse)")
-
4:44 - Loading catalog search top results
// Loading catalog search top results var searchRequest = MusicCatalogSearchRequest( term: "Hello", types: [ Artist.self, Album.self, Song.self ] ) searchRequest.includeTopResults = true let searchResponse = try await searchRequest.response() print("\(searchResponse.topResults)")
-
5:09 - Loading search suggestions
// Loading suggestions let request = MusicCatalogSearchSuggestionsRequest(term: "shaz") let response = try await request.response() print("\(response)")
-
6:30 - Loading catalog top charts
// Loading catalog top charts. let request = MusicCatalogChartsRequest( kinds: [.dailyGlobalTop, .mostPlayed, .cityTop], types: [Song.self, Playlist.self] ) let response = try await request.response() print("\(response.playlistCharts.first)")
-
8:10 - Loading audio variants
// Loading audio variants let album = … let detailedAlbum = try await album.with(.audioVariants) print("\(detailedAlbum.debugDescription)")
-
9:09 - Showing currently playing audio variants
// Showing currently playing audio variants @ObservedObject var musicPlayerQueue = ApplicationMusicPlayer.shared.queue @ObservedObject var musicPlayerState = ApplicationMusicPlayer.shared.state var body: some View { if let currentEntry = musicPlayerQueue.currentEntry { VStack { MyPlayerEntryView(currentEntry) if musicPlayerState.audioVariant == .dolbyAtmos { Image("dolby-atmos-badge") } } } }
-
10:28 - Loading recently played containers
// Loading recently played containers let request = MusicRecentlyPlayedContainerRequest() let response = try await request.response() print("\(response)")
-
10:41 - Loading recently played songs
// Loading recently played songs let request = MusicRecentlyPlayedRequest<Song>() let response = try await request.response() print("\(response)")
-
11:21 - Loading personal recommendations and printing first recommendation
// Loading personal recommendations let request = MusicPersonalRecommendationsRequest() let response = try await request.response() print("\(response.recommendations.first)")
-
11:51 - Loading personal recommendations and printing second recommendation
// Loading personal recommendations let request = MusicPersonalRecommendationsRequest() let response = try await request.response() print("\(response.recommendations[1])")
-
13:36 - Loading library playlists
@MainActor private func loadLibraryPlaylists() async throws { let request = MusicLibraryRequest<Playlist>() let response = try await request.response() self.response = response }
-
14:23 - Displaying library playlists
List { Section(header: Text("Library Playlists").fontWeight(.semibold)) { ForEach(response.items) { playlist in PlaylistCell(playlist) } } }
-
15:47 - Fetching all albums in the library
// Fetching all albums in the library let request = MusicLibraryRequest<Album>() let response = try await request.response() print("\(response)")
-
16:38 - Fetching all compilations in the library
// Fetching all compilations in the library var request = MusicLibraryRequest<Album>() request.filter(matching: \.isCompilation, equalTo: true) let response = try await request.response() print("\(response)")
-
17:08 - Fetching all dance compilations in the library
// Fetching all dance compilations in the library var request = MusicLibraryRequest<Album>() request.filter(matching: \.isCompilation, equalTo: true) request.filter(matching: \.genres, contains: danceGenre) let response = try await request.response() print("\(response)")
-
17:29 - Fetching all downloaded dance compilations in the library
// Fetching all downloaded dance compilations in the library var request = MusicLibraryRequest<Album>() request.filter(matching: \.isCompilation, equalTo: true) request.filter(matching: \.genres, contains: danceGenre) request.includeDownloadedContentOnly = true let response = try await request.response() print("\(response)")
-
18:29 - Fetching all albums sectioned by genre
// Fetching all albums sectioned by genre var request = MusicLibrarySectionedRequest<Genre, Album>() let response = try await request.response() print("\(response)")
-
19:04 - Fetching all albums sectioned by genre sorted by artist name
// Fetching all albums sectioned by genre sorted by artist name var request = MusicLibrarySectionedRequest<Genre, Album>() request.sortItems(by: \.artistName, ascending: true) let response = try await request.response() print("\(response)")
-
20:58 - Fetching relationships using the with method without a preferred source
// Fetching relationships using the with method let album = … let detailedAlbum = try await album.with(.tracks) print("\(album.tracks)")
-
21:11 - Fetching relationships using the with method and a preferred source
// Fetching relationships using the with method let album = … let detailedAlbum = try await album.with(.tracks, preferredSource: .library) print("\(album.tracks)")
-
22:09 - Adding a track to a playlist
Task { try await MusicLibrary.shared(add: selectedTrack, to: playlist) isShowingPlaylistPicker = false }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.