-
Conoce el framework NowPlaying
Echa un primer vistazo a NowPlaying, un framework de Swift que conecta la reproducción multimedia de tu app con elementos del sistema como la pantalla bloqueada, el Centro de Control, Dynamic Island y CarPlay. Descubre cómo publicar el estado de reproducción y responder a comandos utilizando su API observable. Explora las sesiones de reproducción remota, una nueva capacidad que permite a tu app mostrar el contenido multimedia que se reproduce en dispositivos externos e integrar todos los controles de reproducción en esas mismas interfaces del sistema.
Capítulos
- 0:00 - Introducción
- 1:08 - Sesiones multimedia
- 5:03 - Sesiones multimedia remotas
- 10:31 - Extensiones para compartir contenido
Recursos
-
Buscar este video…
-
-
1:57 - Existing PlayerModel implementation
import Observation @Observable final class PlayerModel { let player: SoundPlayer var sound: Sound { player.currentSound } init(player: SoundPlayer) { self.player = player } } -
2:06 - Adopt MediaSessionRepresentable
import NowPlaying extension PlayerModel: MediaSessionRepresentable { var id: String { "ambient-sound-session" } var content: (any MediaContentRepresentable)? { return GenericContent( id: sound.id, title: sound.name, subtitle: sound.description, type: .audio, duration: .live, artwork: Artwork(id: sound.id) { size in let data = try await self.artworkData(size: size) return try ArtworkRepresentation(data: data) } ) } var playbackSnapshot: MediaPlaybackSnapshot? { MediaPlaybackSnapshot( state: player.isPlaying ? .playing() : .paused ) } var commands: [MediaCommand] {[ .play { self.player.play() }, .pause { self.player.pause() }, .previous { self.player.previous() }, .next { self.player.next() } ]} } -
4:31 - MediaSession initialization
import NowPlaying struct PlayerController { let player: SoundPlayer let model: PlayerModel let session: MediaSession<PlayerModel> init() { self.player = SoundPlayer() self.model = PlayerModel(player: player) self.session = MediaSession(model) } } -
6:42 - App extension entry point
import ExtensionFoundation import NowPlaying @main final class SampleAppExtension: @MainActor RemoteMediaSessionExtension { var configuration: some AppExtensionConfiguration { RemoteMediaSessionExtensionConfiguration(extension: self) } var extensionPoint: AppExtensionPoint { AppExtensionPoint.Identifier(host: "com.apple.nowplaying", name: "remote-media") } func session(_ state: RemotePlayerState) async throws -> RemotePlayerModel { RemotePlayerModel(state: state) } } -
7:23 - Existing RemotePlayerModel implementation
import Observation @Observable @MainActor final class RemotePlayerModel { let client: ServerClient var state: RemotePlayerState init(state: RemotePlayerState) { self.client = ServerClient(sessionID: state.sessionID) self.state = state } } -
7:40 - Adopt RemoteMediaSessionRepresentable in app extension
import NowPlaying extension RemotePlayerModel: @MainActor RemoteMediaSessionRepresentable { var id: String { state.sessionID } var content: (any MediaContentRepresentable)? { GenericContent( id: state.sound.id, title: state.sound.name, subtitle: state.sound.description, type: .audio, duration: .live, artwork: Artwork(id: state.sound.id) { size in let data = try await self.artworkData(size: size) return try ArtworkRepresentation(data: data) } ) } var playbackSnapshot: MediaPlaybackSnapshot? { MediaPlaybackSnapshot( state: state.isPlaying ? .playing() : .paused ) } var commands: [MediaCommand] {[ .play { try await self.client.send(.play) }, .pause { try await self.client.send(.pause) }, .previous { try await self.client.send(.previous) }, .next { try await self.client.send(.next) } ]} var devices: [MediaDevice] { state.devices.map { device in MediaDevice( id: device.id, name: device.name, type: .speaker, capabilities: [ .absoluteVolume(device.volume) { volume in // send volume change to server } ] ) } } func update(_ state: RemotePlayerState) { self.state = state } }
-