AVPlayer & SwiftUI Part 3: More Player Controls
Update: Check out part 5 for SwiftUI’s own VideoPlayer view in iOS 14!
Note: video playback on the simulator seems to not show the video, just play the audio, so use a real device if necessary!
This blog is building on the SwiftUI video player that’s been progressing over part 1 and part 2 so make sure you’ve read those first!
In part 2 we were adding in controls for the player, we got the basics done but I was rather stuck on how to get the seek bar (a Slider) to move along automatically as the video progressed, to show where in the video we were watching.
Since then I’ve attended Paul Hudson’s new Hacking With Swift Live conference and the 9th iOSDevUK conference (both highly recommended) and learned more about SwiftUI and iOS 13. In particular it was during Daniel Steinberg’s talk ‘Swift UI — Our story so far’ at iOSDevUK where I stumbled upon the solution to my problem, and Daniel even then went on to confirm just that.
In parts 1 & 2 I was working with Xcode 11 beta 3 (or thereabouts). 3 months later we’ve gone through many more betas and I’m now on Xcode 11 beta 7 (that’s what the Apple site called it anyway… in ‘About Xcode’ it says beta 6 so…)
The problem in part 2
Part 2 left off with me being given a segmention fault while compiling, which wasn’t very useful. I raised it with Apple and never got a response but I believe it was around Xcode 11 beta 5 that the segmentation fault got turned into a compile error, which was much more useful!
It turns out that I was trying to alter a @State var outside of the body closure, which you’re not allowed to do. But I was still stuck, not knowing how to get this @State var to update so the Slider would update as the video progressed.
The solution
The solution is @Binding. When you create a Slider you pass it your @State var, prefixed with $ to create a Binding<Double> (for example). The Slider can then react to changes to your @State var and also update it when it needs to (i.e. when the user interacts with it).
This is what I needed to be able to update the @State var outside of the body of the struct that created it.
I did a nice step by step guide in parts 1 & 2 to get things working but now I’m just going to direct you to the final code on my GitHub and hopefully it’s easy enough to follow, feel free to ask any questions if not!
The quality of the solution?
To be perfectly honest I don’t know if this is the 100% correct solution. There may be other ways to achieve it that may be better, but this seems to work pretty well so hopefully it helps someone!
From what I’ve done with SwiftUI so far it seems to be making me think a lot more about how to structure my code and in a lot of ways that’s because it’s forcing me to because it’s all struct-based and immutable for the most part. I think that’s a good thing, but it’s certainly been frustrating and a fair learning curve to get this far!
There’s certainly some things I’d like to improve about this solution. I don’t think I should be observing the duration and player’s time in the VideoPlayerUIView as it doesn’t have any need for those values. It’s the VideoPlayerControlsView that wants the values but due to the struct-based approach I’ve not yet figured out a way to refactor the solution (though I have some ideas).
In the interest of tying up the loose ends of part 2 of this blog series I wanted to get this published in its current workable form so here it is!
But you’ll be glad to hear that now, part 4 offers a cleaner alternative to observing the player’s time and duration!