Sample CodeiOS 27.0+, iPadOS 27.0+, Mac Catalyst 27.0+Reviewed 2026-07-21View on Apple Developer

Integrating your music app with Apple Intelligence

At a glance

Item Summary
Purpose Integrate audio, playlist, alarm, timer, sharing, Spotlight, and Now Playing experiences with App Intents.
Architecture A manager-oriented SwiftUI/SwiftData app plus a widget extension for a Home Screen widget and Live Activities.
State strategy The app entry owns the process graph; specialized managers own data, playback, timers, alarms, rendering, and system sessions.
Boundary Main app, widget extension, and UI-test targets; shared ActivityKit payload types cross the app/widget target boundary.

Project structure

CosmoTunes/
├── AppIntents/
│   ├── Audio/                  # Entities, queries, playback/share intents
│   ├── Clock/                  # Alarm and timer contracts
│   └── TestSupport/
├── Managers/                  # Data, playback, indexing, alarms, rendering
├── Model/                     # SwiftData and ActivityKit values
├── Views/                     # Chat, library, player, alarms, settings
└── CosmoTunesApp.swift        # Composition root
CosmoTunesWidget/              # Widget and Live Activity target
CosmoTunesUITests/             # Entity, intent, Spotlight, UI tests

The sample is code-rich, but its top-level organization is consistent: domain contracts under AppIntents, durable values under Model, long-lived capabilities under Managers, and target-specific presentation under app or widget folders.

Overall architecture

Reference code

CosmoTunes/CosmoTunesApp.swift:92 — the composition root exposes selected managers to all intent and query invocations.

AppDependencyManager.shared.add(dependency: navigation)
AppDependencyManager.shared.add(dependency: playback)
AppDependencyManager.shared.add(dependency: timerController)
AppDependencyManager.shared.add(dependency: model)
AppDependencyManager.shared.add(dependency: donations)
AppDependencyManager.shared.add(dependency: alarmScheduler)
AppDependencyManager.shared.add(dependency: userPreferences)

The root performs wiring, while each manager keeps one operational concern. The widget is a separate executable boundary and consumes shared ActivityKit values rather than the app’s live manager graph.

Ownership and state

Ownership evidence

CosmoTunes/CosmoTunesApp.swift:56 — the root wires non-owning cross-links after creating every long-lived manager.

playback.donations = donations
playback.audioEngine.mediaSession = mediaSession
playback.mediaSession = mediaSession
mediaSession.playback = playback
mediaSession.artworkRenderer = artworkRenderer
Owner Object or state Relationship Mutation authority
CosmoTunesApp Manager graph and MediaSession wrapper Creates and strongly retains Composition and lifecycle only
ModelManager ModelContext, generation service, SpotlightIndexer Strong ownership; weak optional collaborators Persistent library/chat/alarm state and indexing changes
PlaybackController AudioEngine, queue, current track/playlist Strong subsystem owner Queue and transport state
AudioEngine AVAudio engine, sampler, scheduling task Private implementation ownership Low-level playback timing and engine state
CosmoTunesMediaSession Tracked Now Playing snapshot Strongly retained by app, weak link back to playback System-facing media state and commands
Widget extension Widget and Live Activity configurations Separate process/target lifecycle Presentation of timeline/activity values

Class and protocol design

Type Design Responsibility
ModelManager @MainActor @Observable final class Coordinate SwiftData and hand indexing work to an actor.
PlaybackController @MainActor @Observable final class Own the queue and translate user/system commands into engine actions.
AudioEngine @MainActor @Observable final class Encapsulate AVFoundation setup, note scheduling, and playback timing.
CosmoTunesMediaSession MediaSessionRepresentable Adapt playback state and app entity identifiers to Now Playing.
PlayAudioIntent AudioPlaybackIntent Resolve a schema AudioEntity and delegate queue mutations.
SongEntity / SongQuery IndexedEntity, Transferable, query protocols Bridge stored tracks to Spotlight, Siri lookup, and file transfer.
SpotlightWrapping app-defined Sendable protocol Make Core Spotlight batching replaceable in tests.

This sample combines framework protocol conformances with one purposeful app-defined seam: SpotlightWrapping is injected into SpotlightIndexer, while concrete playback managers remain directly composed.

Access control

Symbol Access Verified effect Likely rationale
Playback queue snapshots private(set) Views and system adapters can read current notes/queue/index/playlist; only PlaybackController writes them. Preserve queue invariants.
AVAudio engine internals private Engine, sampler, scheduler, and timing state cannot be bypassed by callers. Keep AVFoundation lifecycle coherent.
SongEntity backing MIDI/tempo private Only the entity’s transfer implementation reads export snapshots. Hide non-schema transport data.
Cross-manager collaborators weak Managers may call peers without extending their lifecycle. Avoid cycles in the bidirectional playback/session graph.
SleepTimerAttributes payload members public inside an internal outer type ActivityKit-facing fields and initializers are explicit; the enclosing type is still not a public library API. Make the shared serialization surface clear.
Preview/query helpers fileprivate Widget preview fixtures and alarm enrichment are shared only within their files. Keep helper APIs out of target-wide use.
Managers, entities, intents internal default; no open App and extension targets compile their own selected sources without exposing subclassable APIs. Executable targets do not need a public framework surface.

Reference code

CosmoTunes/Managers/PlaybackController.swift:30 — readable playback snapshots have a single mutation owner.

private(set) var currentTrackNotes: [MIDINoteData] = []
private(set) var playQueue: [GeneratedTrack] = []
private(set) var queueIndex = 0
private(set) var currentPlaylist: Playlist?

weak var donations: DonationManager?
weak var mediaSession: CosmoTunesMediaSession?

Logic ownership and placement

Logic Owner Placement rationale
Object creation and cross-links CosmoTunesApp One visible composition root controls lifetimes and dependency registration.
SwiftData, generation routing, Spotlight change creation ModelManager Persistent mutations share one main-actor context.
Spotlight I/O SpotlightIndexer plus SpotlightWrapping Actor isolation avoids blocking UI and the protocol supports recording tests.
Queue and transport PlaybackController Higher-level playback invariants remain above AVFoundation details.
Sampling and timing AudioEngine Low-level audio resources stay encapsulated.
Lock Screen/control commands CosmoTunesMediaSession One adapter synchronizes app state with the Now Playing framework.
Parameter resolution App Intent/entity query files System values are converted before invoking domain managers.
Widget and Live Activity views CosmoTunesWidget target Extension presentation is isolated from the app’s runtime graph.

Design patterns

Pattern Source evidence Purpose or tradeoff
Composition root / DI CosmoTunes/CosmoTunesApp.swift:34 Makes a large object graph explicit and shares selected services with intents.
Subsystem controller CosmoTunes/Managers/PlaybackController.swift:15 Separates queue policy from audio implementation.
Protocol adapter CosmoTunes/Managers/CosmoTunesMediaSession.swift:165 Presents app playback through a framework-defined contract.
Weak back-reference CosmoTunes/Managers/CosmoTunesMediaSession.swift:48 Enables remote commands without a retain cycle.
Replaceable gateway CosmoTunes/Managers/CoreSpotlightWrapper.swift:136 Allows deterministic indexing tests behind SpotlightWrapping.
Multi-target shared value CosmoTunes/Model/SleepTimerAttributes.swift:16 Shares ActivityKit payload shape without sharing live managers.

Naming conventions

  • Long-lived capabilities use role suffixes: Manager, Controller, Engine, Renderer, Indexer, and Session.
  • Framework projections use Entity; framework actions use verb-noun Intent names.
  • Methods describe commands at the correct layer: playTracks, loadAndPlay, publishSnapshot, and performChange.
  • Read-only state names describe snapshots (currentTrackNotes, playQueue, playbackSnapshot).
  • Files match primary types, while extensions group protocol conformances such as EntityQuery, Transferable, and MediaSessionRepresentable.

Architecture takeaways

  • Keep a large sample understandable by assigning state to narrowly named subsystem owners and doing all cross-wiring at the app root.
  • Put queue policy above the AVFoundation engine and expose read-only snapshots with private(set).
  • Use weak links where two coordinators need to call each other, while one root strongly owns both.
  • Share immutable ActivityKit payload types with the widget; do not attempt to share the app’s live dependency graph across targets.
  • Add a custom protocol only where replacement materially improves testing, as with Spotlight I/O.

Source map

Source Relevant symbols
CosmoTunes/CosmoTunesApp.swift:14 App composition root and dependency registration
CosmoTunes/Managers/ModelManager.swift:26 Data owner and indexing coordination
CosmoTunes/Managers/PlaybackController.swift:15 Queue and transport owner
CosmoTunes/Managers/AudioEngine.swift:20 AVFoundation implementation boundary
CosmoTunes/Managers/CosmoTunesMediaSession.swift:26 Now Playing adapter
CosmoTunes/AppIntents/Audio/Intents/PlayAudioIntent.swift:15 Audio schema orchestration
CosmoTunes/AppIntents/Audio/Entities/SongEntity.swift:17 Indexed and transferable song adapter
CosmoTunesWidget/CosmoTunesWidgetBundle.swift:11 Widget extension entry