axiom-now-playing-musickit

MusicKit Integration (Apple Music)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "axiom-now-playing-musickit" with this command: npx skills add charleswiltgen/axiom/charleswiltgen-axiom-axiom-now-playing-musickit

MusicKit Integration (Apple Music)

Time cost: 5-10 minutes

Key Insight

MusicKit's ApplicationMusicPlayer automatically publishes to MPNowPlayingInfoCenter. You don't need to manually update Now Playing info when playing Apple Music content.

What's Automatic

When using ApplicationMusicPlayer :

  • Track title, artist, album

  • Artwork (Apple's album art)

  • Duration and elapsed time

  • Playback rate (playing/paused state)

The system handles all MPNowPlayingInfoCenter updates for you.

What's NOT Automatic

  • Custom metadata (chapter markers, custom artist notes)

  • Remote command customization beyond standard controls

  • Mixing MusicKit content with your own content

Subscription and Authorization

Check Music Authorization

import MusicKit

func requestMusicAccess() async -> Bool { let status = await MusicAuthorization.request() return status == .authorized }

// Check current status without prompting let currentStatus = MusicAuthorization.currentStatus // .authorized, .denied, .notDetermined, .restricted

Check Apple Music Subscription

func checkSubscription() async -> Bool { do { let subscription = try await MusicSubscription.current return subscription.canPlayCatalogContent } catch { return false } }

// Observe subscription changes func observeSubscription() { Task { for await subscription in MusicSubscription.subscriptionUpdates { if subscription.canPlayCatalogContent { // Full Apple Music access } else if subscription.canBecomeSubscriber { // Show subscription offer showSubscriptionOffer() } } } }

Subscription Offer Sheet

import MusicKit import StoreKit

// Present Apple Music subscription offer MusicSubscriptionOffer.Options( messageIdentifier: .playMusic, itemID: song.id )

// In SwiftUI .musicSubscriptionOffer(isPresented: $showOffer, options: offerOptions)

Graceful Fallback Without Subscription

@MainActor class MusicPlayer: ObservableObject { @Published var canPlay = false

func handlePlayRequest(song: Song) async {
    let authorized = await requestMusicAccess()
    guard authorized else {
        showAuthorizationDeniedAlert()
        return
    }

    do {
        let subscription = try await MusicSubscription.current
        if subscription.canPlayCatalogContent {
            // Full playback
            try await play(song: song)
        } else {
            // Preview only (30-second clips)
            if let previewURL = song.previewAssets?.first?.url {
                playPreview(url: previewURL)
            }
        }
    } catch {
        handleError(error)
    }
}

}

Playback

Basic Playback

import MusicKit

@MainActor class MusicKitPlayer { private let player = ApplicationMusicPlayer.shared

func play(song: Song) async throws {
    // ✅ Just play - MPNowPlayingInfoCenter updates automatically
    player.queue = [song]
    try await player.play()

    // ❌ DO NOT manually set nowPlayingInfo here
    // MPNowPlayingInfoCenter.default().nowPlayingInfo = [...] // WRONG!
}

func pause() {
    player.pause()
}

func stop() {
    player.stop()
}

}

Observing Playback State

@MainActor class PlayerViewModel: ObservableObject { private let player = ApplicationMusicPlayer.shared @Published var isPlaying = false @Published var currentEntry: ApplicationMusicPlayer.Queue.Entry? @Published var playbackTime: TimeInterval = 0

func observeState() {
    // Observe playback status
    Task {
        for await state in player.state.objectWillChange.values {
            isPlaying = player.state.playbackStatus == .playing
        }
    }

    // Observe current entry (track changes)
    Task {
        for await queue in player.queue.objectWillChange.values {
            currentEntry = player.queue.currentEntry
        }
    }
}

}

Queue Management

Setting the Queue

let player = ApplicationMusicPlayer.shared

// Single song player.queue = [song]

// Album player.queue = ApplicationMusicPlayer.Queue(album: album)

// Playlist player.queue = ApplicationMusicPlayer.Queue(playlist: playlist)

// Multiple items player.queue = ApplicationMusicPlayer.Queue(for: [song1, song2, song3])

// Start at specific item player.queue = ApplicationMusicPlayer.Queue(for: songs, startingAt: songs[2])

Queue Operations

// Skip to next try await player.skipToNextEntry()

// Skip to previous try await player.skipToPreviousEntry()

// Restart current track player.restartCurrentEntry()

// Append to queue try await player.queue.insert(song, position: .afterCurrentEntry) try await player.queue.insert(song, position: .tail) // End of queue

// Shuffle and repeat player.state.shuffleMode = .songs // .off, .songs player.state.repeatMode = .all // .none, .one, .all

Observing Queue Changes

// Current track info if let entry = player.queue.currentEntry { let title = entry.title let subtitle = entry.subtitle // Artist name let artwork = entry.artwork // Artwork for display

// Get full Song object if needed
if case .song(let song) = entry.item {
    let albumTitle = song.albumTitle
}

}

Hybrid Apps (Own Content + Apple Music)

If your app plays both Apple Music and your own content:

import MusicKit

@MainActor class HybridPlayer { private let musicKitPlayer = ApplicationMusicPlayer.shared private var avPlayer: AVPlayer? private var currentSource: ContentSource = .none

enum ContentSource {
    case none
    case appleMusic      // MusicKit handles Now Playing
    case ownContent  // We handle Now Playing
}

func playAppleMusicSong(_ song: Song) async throws {
    // Switch to MusicKit
    avPlayer?.pause()
    currentSource = .appleMusic

    musicKitPlayer.queue = [song]
    try await musicKitPlayer.play()
    // ✅ MusicKit handles Now Playing automatically
}

func playOwnContent(_ url: URL) {
    // Switch to AVPlayer
    musicKitPlayer.pause()
    currentSource = .ownContent

    avPlayer = AVPlayer(url: url)
    avPlayer?.play()

    // ✅ Manually update Now Playing (see axiom-now-playing)
    updateNowPlayingForOwnContent()
}

private func updateNowPlayingForOwnContent() {
    var nowPlayingInfo = [String: Any]()
    nowPlayingInfo[MPMediaItemPropertyTitle] = "My Track"
    // ... rest of manual setup
    MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}

}

Common Mistake

// ❌ WRONG - Overwrites MusicKit's automatic Now Playing data func playAppleMusicSong(_ song: Song) async throws { try await ApplicationMusicPlayer.shared.play()

// ❌ This clears MusicKit's Now Playing info!
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = song.title
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo

}

// ✅ CORRECT - Let MusicKit handle it func playAppleMusicSong(_ song: Song) async throws { try await ApplicationMusicPlayer.shared.play() // That's it! MusicKit publishes Now Playing automatically. }

When to Use Manual Updates with MusicKit

Only override MPNowPlayingInfoCenter if:

  • You're mixing in additional metadata (e.g., podcast chapter markers)

  • You're displaying custom content alongside Apple Music

  • You have a specific reason to replace MusicKit's automatic behavior

Default: Let MusicKit manage Now Playing automatically.

Resources

Docs: /musickit, /musickit/applicationmusicplayer, /musickit/musicsubscription

Skills: axiom-now-playing, axiom-now-playing-carplay

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

axiom-vision

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftdata

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-26-ref

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-architecture

No summary provided by upstream source.

Repository SourceNeeds Review