Contents

RemoteMediaSessionRepresentable

A session that plays remotely, potentially across multiple devices.

Declaration

@MainActor protocol RemoteMediaSessionRepresentable : Identifiable

Mentioned 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)
    }
    // …
}

Topics

Associated Types

Instance Properties

Instance Methods

See Also

Remote sessions