RemoteMediaSessionRepresentable
A session that plays remotely, potentially across multiple devices.
Declaration
@MainActor protocol RemoteMediaSessionRepresentable : IdentifiableMentioned in
Overview
Conform to this protocol to represent playback happening outside of the current device. This object is expected to interact with a remote device, and thus may require some form of remote connection that you set up on initialization.
Your session provides metadata and supported commands, as well as the list of devices playing in this session. It also handles volume changes and commands targeting the remote session.
Your app donates remote sessions to the system in two ways: from the main app, or through push notifications.
See RemoteMediaSession for the in-app API to donate a remote session.
The following example shows a basic implementation:
// A struct representing the current playback information from a remote device.
struct PlayerSnapshot: Codable {
let trackId: String
let title: String
let artist: String
let album: String
let duration: Double
let elapsedTime: Double
let timestamp: Date
let isPlaying: Bool
let playingDevices: [PlayingDevice]
}
struct PlayingDevice: Codable {
let id: String
let name: String
let volume: Float
}
@Observable
class RemotePlayerModel {
var server: ServerConnection
var snapshot: PlayerSnapshot
}
class MySession: RemoteMediaSessionRepresentable {
struct Attributes: RemoteMediaSessionAttributes {
let id: String
var snapshot: PlayerSnapshot
}
let id: String
var remotePlayer: RemotePlayerModel
var devices: [MediaDevice] {
remotePlayer.snapshot.playingDevices.map { device in
MediaDevice(
id: device.id,
name: device.name,
type: .speaker,
capabilities: [
.absoluteVolume(device.volume) { newLevel in
// Communicate with an external server to issue the volume change
try await remotePlayer.server.setVolume(newLevel, forDevice: device.id)
}
]
)
}
}
var content: (any MediaContentRepresentable)? {
let snapshot = remotePlayer.snapshot
return MusicContent(
id: snapshot.trackId,
songTitle: snapshot.title,
artistName: snapshot.artist,
albumName: snapshot.album,
type: .audio,
duration: .finite(snapshot.duration),
artwork: Artwork(id: snapshot.trackId) { size in
let data = await remotePlayer.server.loadArtworkData(for: snapshot.trackId, size: size)
return try ArtworkRepresentation(data: data)
}
)
}
var playbackSnapshot: MediaPlaybackSnapshot {
let snapshot = remotePlayer.snapshot
if snapshot.isPlaying {
return MediaPlaybackSnapshot(
state: .playing(rate: 1.0),
elapsedTime: snapshot.elapsedTime,
timestamp: snapshot.timestamp
)
} else {
return MediaPlaybackSnapshot(
state: .paused,
elapsedTime: snapshot.elapsedTime,
timestamp: snapshot.timestamp
)
}
}
var commands: [MediaCommand] {[
.play {
// Communicate with an external server to issue the command
try await remotePlayer.server.play()
},
.pause {
try await remotePlayer.server.pause()
},
// … more supported commands …
]}
func update(_ attributes: Attributes) {
// Incorporate the incoming attributes into your existing model
remotePlayer.update(with: attributes)
}
// …
}