Search code examples
iosswiftiphoneswiftuiavplayer

Get AVPlayerItem track title with SwiftUI


I'm trying to create a radio app, which streams audio from an http streaming.

I'm trying to fetch the current playing track title from the streaming, but all I can find is deprecated methods.

My model looks like this:

struct RadioStreamer {
    let streamingURL: URL

    private let player: AVPlayer?
    private let playerItem: AVPlayerItem?

    init(streamingURL: URL) {
        self.streamingURL = streamingURL

        self.playerItem = AVPlayerItem(url: self.streamingURL)
        self.player = AVPlayer(playerItem: self.playerItem)

    }

    public func playStreaming() { self.player?.play() }
}

My contentView looks like this:

let streamer = RadioStreamer(streamingURL: URL(string: "MY-STREAMING-URL")!)

var body: some View {
    VStack {
        Text("HERE I WANT THE TRACK TITLE")

        Button(action: {
            self.streamer.playStreaming()
        }) {
            Text("Play")
        }
    }
}

How can I fetch the track title with a non-deprecated method in SwiftUI?

Thank you!


Solution

  • This is the case when reference type is more preferable. So here is simplified demo of possible approach

    import AVKit
    import Combine
    
    class RadioStreamer: NSObject, ObservableObject {
        @Published var itemTitle: String = "Unknown"
    
        let streamingURL: URL
    
        private let player: AVPlayer?
        private let playerItem: AVPlayerItem?
    
        init(streamingURL: URL) {
            self.streamingURL = streamingURL
            self.playerItem = AVPlayerItem(url: self.streamingURL)
            self.player = AVPlayer(playerItem: self.playerItem)
    
            super.init()
    
            // setup output for player item metadata
            let metaOutput = AVPlayerItemMetadataOutput(identifiers: [ // nil, for all
                // specify needed meta to be output, 
                AVMetadataIdentifier.commonIdentifierTitle.rawValue     
            ])
            metaOutput.setDelegate(self, queue: DispatchQueue.main)
            self.playerItem?.add(metaOutput)
        }
    
        public func playStreaming() { self.player?.play() }
    }
    
    extension RadioStreamer: AVPlayerItemMetadataOutputPushDelegate {
        func metadataOutput(_ output: AVPlayerItemMetadataOutput, didOutputTimedMetadataGroups groups: [AVTimedMetadataGroup], from track: AVPlayerItemTrack?) {
    
            // simplest demo, in common case iterate all groups and all items in group
            // to find what you need if you requested many metadata
            if let group = groups.first,
                let item = group.items.first {
                self.itemTitle = item.stringValue ?? "Unknown"
            }
        }
    }