Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Overflow reference for the musickit skill. Contains advanced patterns that exceed the main skill file's scope.
@Observableimport MusicKit
import MediaPlayer
@Observable
@MainActor
final class MusicPlayerManager {
let player = ApplicationMusicPlayer.shared
var currentSong: Song?
var isPlaying = false
var playbackTime: TimeInterval = 0
var queue: [Song] = []
var hasSubscription = false
func setup() async {
// Check authorization
let status = await MusicAuthorization.request()
guard status == .authorized else { return }
// Check subscription
if let subscription = try? await MusicSubscription.current {
hasSubscription = subscription.canPlayCatalogContent
}
// Observe subscription changes
Task {
for await subscription in MusicSubscription.subscriptionUpdates {
hasSubscription = subscription.canPlayCatalogContent
}
}
}
func play(_ song: Song) async throws {
guard hasSubscription else { return }
player.queue = [song]
try await player.play()
currentSong = song
isPlaying = true
}
func togglePlayPause() {
if player.state.playbackStatus == .playing {
player.pause()
isPlaying = false
} else {
Task {
try? await player.play()
isPlaying = true
}
}
}
func skip() async {
try? await player.skipToNextEntry()
}
}import SwiftUI
import MusicKit
struct MiniPlayerView: View {
@Environment(MusicPlayerManager.self) private var manager
var body: some View {
HStack {
if let song = manager.currentSong {
if let artwork = song.artwork {
ArtworkImage(artwork, width: 44, height: 44)
.clipShape(.rect(cornerRadius: 6))
}
VStack(alignment: .leading) {
Text(song.title)
.font(.subheadline)
.lineLimit(1)
Text(song.artistName)
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
Button {
manager.togglePlayPause()
} label: {
Image(systemName: manager.isPlaying ? "pause.fill" : "play.fill")
.font(.title2)
}
Button {
Task { await manager.skip() }
} label: {
Image(systemName: "forward.fill")
}
}
}
.padding(.horizontal)
.frame(height: 60)
}
}struct MusicSearchView: View {
@State private var searchText = ""
@State private var results: MusicItemCollection<Song> = []
@Environment(MusicPlayerManager.self) private var manager
var body: some View {
NavigationStack {
List(results) { song in
Button {
Task { try? await manager.play(song) }
} label: {
HStack {
if let artwork = song.artwork {
ArtworkImage(artwork, width: 50, height: 50)
.clipShape(.rect(cornerRadius: 4))
}
VStack(alignment: .leading) {
Text(song.title)
Text(song.artistName)
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
}
.searchable(text: $searchText, prompt: "Search Apple Music")
.onChange(of: searchText) {
Task { await search() }
}
.navigationTitle("Search")
}
}
private func search() async {
guard !searchText.isEmpty else {
results = []
return
}
var request = MusicCatalogSearchRequest(term: searchText, types: [Song.self])
request.limit = 25
if let response = try? await request.response() {
results = response.songs
}
}
}func fetchTopSongs() async throws -> MusicItemCollection<Song> {
var request = MusicCatalogChartsRequest(kinds: [.mostPlayed], types: [Song.self])
request.limit = 50
let response = try await request.response()
return response.songCharts.first?.items ?? []
}func fetchSongsByGenre(_ genre: Genre) async throws -> MusicItemCollection<Song> {
var request = MusicCatalogSearchRequest(term: genre.name, types: [Song.self])
request.limit = 25
let response = try await request.response()
return response.songs
}func addToLibrary(_ song: Song) async throws {
try await MusicLibrary.shared.add(song)
}
func addAlbumToLibrary(_ album: Album) async throws {
try await MusicLibrary.shared.add(album)
}func fetchLibrarySongs() async throws -> MusicItemCollection<Song> {
var request = MusicLibraryRequest<Song>()
request.sort(by: \.lastPlayedDate, ascending: false)
request.limit = 50
let response = try await request.response()
return response.items
}func fetchPlaylists() async throws -> MusicItemCollection<Playlist> {
var request = MusicLibraryRequest<Playlist>()
request.sort(by: \.lastModifiedDate, ascending: false)
let response = try await request.response()
return response.items
}func addToPlaylist(_ playlist: Playlist, songs: [Song]) async throws {
try await MusicLibrary.shared.add(songs, to: playlist)
}Use MPNowPlayingSession when your app manages multiple simultaneous audio
sessions (e.g., picture-in-picture video plus background music).
import MediaPlayer
import AVFoundation
func createNowPlayingSession(for player: AVPlayer) -> MPNowPlayingSession {
let session = MPNowPlayingSession(players: [player])
// Session-scoped remote command center
session.remoteCommandCenter.playCommand.addTarget { _ in
player.play()
return .success
}
session.remoteCommandCenter.pauseCommand.addTarget { _ in
player.pause()
return .success
}
// Session-scoped now playing info
session.nowPlayingInfoCenter.nowPlayingInfo = [
MPMediaItemPropertyTitle: "Track Title",
MPMediaItemPropertyArtist: "Artist Name"
]
// Activate this session to become the "now playing" app
session.becomeActiveIfPossible { success in
print("Now playing session active: \(success)")
}
return session
}Configure the audio session before starting playback to enable background audio.
import AVFoundation
func configureAudioSession() throws {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playback, mode: .default)
try session.setActive(true)
}Add audio to UIBackgroundModes in Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>func observeInterruptions() {
NotificationCenter.default.addObserver(
forName: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance(),
queue: .main
) { notification in
guard let typeValue = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
switch type {
case .began:
// Pause UI, save state
break
case .ended:
let options = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? UInt
if let options, AVAudioSession.InterruptionOptions(rawValue: options).contains(.shouldResume) {
// Resume playback
}
@unknown default:
break
}
}
}Pause playback when headphones are unplugged to avoid unexpected speaker output.
func observeRouteChanges() {
NotificationCenter.default.addObserver(
forName: AVAudioSession.routeChangeNotification,
object: AVAudioSession.sharedInstance(),
queue: .main
) { notification in
guard let reasonValue = notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
return
}
if reason == .oldDeviceUnavailable {
// Headphones were unplugged -- pause playback
pausePlayback()
}
}
}skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-localization
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references