Skip to content

Instantly share code, notes, and snippets.

@markst
Created October 25, 2023 23:04
Show Gist options
  • Save markst/6470df34559439303a481081dc78faf7 to your computer and use it in GitHub Desktop.
Save markst/6470df34559439303a481081dc78faf7 to your computer and use it in GitHub Desktop.
`UIViewRepresentable` wrapper for `AVPlayer`
import Foundation
import UIKit
import AVKit
import SwiftUI
import Combine
class PlayerUIView: UIView {
// MARK: Class Property
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
// MARK: - Helpers
var playerLayer: AVPlayerLayer? {
layer as? AVPlayerLayer
}
var player: AVPlayer? {
get {
return playerLayer?.player
}
set {
playerLayer?.player = newValue
}
}
}
struct PlayerView: UIViewRepresentable {
@Binding var player: AVPlayer
@Binding var visible: Bool
let onVideoCompleted: VoidClosure
let onVideoError: VoidClosure
var videoBackground: UIColor = .clear
var videoGravity: AVLayerVideoGravity = .resizeAspectFill
// MARK: - UIViewRepresentable
func makeUIView(context: Context) -> PlayerUIView {
PlayerUIView()
}
func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
uiView.playerLayer?.videoGravity = videoGravity
uiView.playerLayer?.player = visible ? player : nil
uiView.backgroundColor = videoBackground
}
// MARK: -
func makeCoordinator() -> PlayerView.Coordinator {
Coordinator(self, onVideoCompleted: onVideoCompleted, onVideoError: onVideoError)
}
class Coordinator: NSObject {
let parent: PlayerView
let onVideoCompleted: VoidClosure
private var cancellables = Set<AnyCancellable>()
init(
_ parent: PlayerView,
onVideoCompleted: @escaping VoidClosure,
onVideoError: @escaping VoidClosure
) {
self.parent = parent
self.onVideoCompleted = onVideoCompleted
super.init()
let currentItem = parent.player
.publisher(for: \.currentItem)
.compactMap({ $0 })
currentItem
.flatMapLatest({ NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: $0) })
.share()
.sink(receiveValue: { [onVideoCompleted] _ in
onVideoCompleted()
})
.store(in: &cancellables)
currentItem
.flatMapLatest({ $0.publisher(for: \.status) })
.share()
.filter({ $0 == .failed })
.sink(receiveValue: { [onVideoError] _ in
onVideoError()
})
.store(in: &cancellables)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment