swiftswiftuiavplayeravkitavqueueplayer

How to set an observer on the queue player's currentItem?


I am trying to set an observer for the currentItem of the AVQueueplayer but I get an error called EXC_BAD_ACCESS. Can someone help me please? Thanks for your attention. I’m looking forward to your reply. Here is my code:

struct VideoPlayerS : UIViewControllerRepresentable {
    var work : WorkoutDeS
    @Binding var player : AVQueuePlayer
    var playerLayer = AVPlayerLayer()
    
    public func makeUIViewController(context: Context) -> AVPlayerViewController {
        let items = [
            AVPlayerItem(url: URL(fileURLWithPath: String(work.url1))),
            AVPlayerItem(url: URL(fileURLWithPath: String(work.url2)))
        ]
        
        let player = AVQueuePlayer(items: items)
        let controller = AVPlayerViewController()
        
        DispatchQueue.main.async {
            self.player = player
        }
        
        controller.player = player
        controller.videoGravity = .resizeAspectFill
        
        player.actionAtItemEnd = .none
        
        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { _ in
            self.player.seek(to: CMTime.zero)
            self.player.play()
        }
        
        player.play()
        
        player.currentItem?.addObserver(AVQueuePlayer(), forKeyPath: "status", options: NSKeyValueObservingOptions(), context: nil)
    }
        
    func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            if keyPath == "status" {
                print("Hello")
                
                player.currentItem?.removeObserver(AVQueuePlayer(), forKeyPath: "status")
            }
        }
        
        return controller
    }
    
    func rewindVideo(notification: Notification) {
        playerLayer.player?.seek(to: .zero)
    }
    
    public func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayerS>) {
        
    }

}

Solution

  • Representable, which is struct, cannot be used KVO observer. You can to use Coordinator as observer.

    Here is modified code with possible approach:

    struct VideoPlayerS : UIViewControllerRepresentable {
    
        var work : WorkoutDeS
        @Binding var player : AVQueuePlayer
        var playerLayer = AVPlayerLayer()
    
        public func makeUIViewController(context: Context) -> AVPlayerViewController {
            let items = [
    
                AVPlayerItem(url: URL(fileURLWithPath: String(work.url1))),
                AVPlayerItem(url: URL(fileURLWithPath: String(work.url2)))
    
            ]
            let player = AVQueuePlayer(items: items)
            let controller = AVPlayerViewController()
    
            DispatchQueue.main.async {
                self.player = player
            }
            controller.player = player
            controller.videoGravity = .resizeAspectFill
    
            player.actionAtItemEnd = .none
            NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { _ in
                self.player.seek(to: CMTime.zero)
                self.player.play()
            }
    
            player.currentItem?.addObserver(context.coordinator, forKeyPath: "status", options: [.new], context: nil)
    
            player.play()
            return controller
        }
    
        func makeCoordinator() -> Coordinator {
            Coordinator(owner: self)
        }
    
        class Coordinator: NSObject {
            var owner : VideoPlayerS
    
            init(owner: VideoPlayerS) {
                self.owner = owner
            }
    
            override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
                guard let item = object as? AVPlayerItem else { return }
    
                if keyPath == "status" {
                    print("Hello")
                    item.removeObserver(self, forKeyPath: "status")
                }
            }
        }
    
        func rewindVideo(notification: Notification) {
            playerLayer.player?.seek(to: .zero)
        }
    
        public func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayerS>) {
    
        }
    }