Presenting a VideoPlayer
in a sheet
.sheet(isPresented: $showVideo) {
VideoPlayerView(videoURL: postVideo.videoURL)
}
To get the video to autoplay, I've implemented the following view
struct VideoPlayerView: View {
private var player: AVPlayer
init(videoURL: URL) {
player = AVPlayer(url: videoURL)
}
var body: some View {
VideoPlayer(player: player)
.edgesIgnoringSafeArea(.all)
.onAppear {
player.play()
}
.onDisappear() {
player.pause()
}
}
}
However, checking the Memory Graph, the AVPlayer
is being leaking. Omitting the onDisappear()
player.pause()
line, the video's sound keeps playing even after dismissing the sheet.
Going with the simpler version that doesn't keep an instance of AVPlayer
doesn't have those issues, but then I lose autoplay.
struct VideoPlayerView: View {
let videoURL: URL
var body: some View {
VideoPlayer(player: AVPlayer(url: videoURL))
.edgesIgnoringSafeArea(.all)
}
}
What would be the correct way of achieving autoplay and not leaking?
(Not looking for UIViewRepresentable solution)
You can't init objects in body
because View
structs are just values with no lifetime so any objects you init will just get lost. Instead, your objects need to be init in actions like .onAppear
, .onChange
etc. and stored inside a property wrapper like @State
- which essentially is another object that is given to the same View
every time it is re-init, which you can do as follows:
struct VideoPlayerView: View {
@State var player: AVPlayer? // the same value is given to the VideoPlayerView every time it is init
let url: URL
var body: some View {
Group {
if let player {
VideoPlayer(player: player)
.edgesIgnoringSafeArea(.all)
.onAppear {
player.play()
}
.onDisappear() {
player.pause()
}
}
else {
Text("Invalid") // usually onChange will only occur the first time if there is something can can appear so this is just a placeholder but you can try without this.
}
}
.onChange(of: url, initial: true) { // fyi initial means on every appear
if player?.url != url {
player = AVPlayer(url: url) // setting this state causes body to be called and a new VideoPlayer to be init with this new player
}
}
In case you're wondering how on earth does SwiftUI know which View
to give each @State
value back to - it uses it's path in the View
hierarchy to identify it, so you have to take care not to move it to a different line in body
. The .id
modifier can be used to override the automatic path id.