VideoPlayer in SwiftUI (iOS 14 +)

DevTechie Inc
May 11, 2023

iOS 14 brought AVKit to the world of SwiftUI with the introduction of VideoPlayer view. VideoPlayer displays content of an AVPlayer instance. View is defined inside AVKit framework and lets us play videos natively in SwiftUI without the need of porting functionality over from UIKit.

VideoPlayer takes player as an initialization parameter, which is an instance of AVPlayer.

Let’s start with an example. We will begin with the import of AVKit framework.

import AVKit

Videos can be played from bundle or from a url. We will start by playing video from bundle.

We can download sample videos from pexels.com

https://www.pexels.com/videos/

Import video and make sure that we have copy item if needed, create folder reference and add to targets checked.

Ideally, you would want to import videos in a folder, maybe create a Resources folder.

Now, all we gotta do is create VideoPlayer and AVPlayer instance to play this video.

import AVKitstruct DevTechieVideoPlayerExample: View {
    var body: some View {
        VStack {
            Text("DevTechie")
                .font(.title3)
            VideoPlayer(player: AVPlayer(url: Bundle.main.url(forResource: "sample", withExtension: "mp4")!))
        }
    }
}

Note: VideoPlayer view comes with bunch of controls out of the box.

Playing videos via URLs

Since AVPlayer takes URL as the parameter, we can easily replace the video link from local to remote location and stream the video via remote url. All we need to do, is provide a url to the video.

We will use another video from Pexels by copying its url:

https://www.pexels.com/video/10167684/download/

Our code will look like this:

import AVKitstruct DevTechieVideoPlayerExample: View {
    var body: some View {
        VStack {
            Text("DevTechie")
                .font(.title3)
            VideoPlayer(player: AVPlayer(url: URL(string: "https://www.pexels.com/video/10167684/download/")!))
        }
    }
}

Video Player with Overlay

VideoPlayer has another overload which provides a ViewBuilder closure to display content as overlay.

Let’s add overlay to our example.

struct DevTechieVideoPlayerExample: View {
    var body: some View {
        VideoPlayer(player: AVPlayer(url: Bundle.main.url(forResource: "sample", withExtension: "mp4")!)) {
            VStack {
                Text("Visit DevTechie.com to learn iOS Development")
                    .font(.largeTitle)
                    .frame(maxWidth: .infinity)
                    .background(.thinMaterial)
                Spacer()
            }
        }
        .ignoresSafeArea()
    }
}

Auto Play Video

At this point, we have to tap on play button when view is loaded but what if we want to auto play the video as soon as view is loaded and video is ready to be played. We can do all that with a simple modification.

We just gotta take out AVPlayer instance into a property and use the instance to call predefined functions to play the video.

let player = AVPlayer(url: URL(string: "https://www.pexels.com/video/10167684/download/")!)

We will call player.play() inside onAppear

struct DevTechieVideoPlayerExample: View {
    let player = AVPlayer(url: URL(string: "https://www.pexels.com/video/10167684/download/")!)
    
    var body: some View {
        VStack {
            Text("DevTechie")
                .font(.title3)
            VideoPlayer(player: player)
        }
        .onAppear {
            player.play()
        }
    }
}

End of video notification

Playing video is great but what if we want to be notified when our video ends. We can do that by observing AVPlayer’s notification. Anytime current video ends, AVPlayer posts a notification in NotificationCenter and we can observe that to act upon it. We can either replay the video or start playing another video.

Let’s update our code to replay the video when it ends.

We will start by creating a publisher to observe AVPlayerItemDidPlayToEndTime notification.

let videoEnded = NotificationCenter.default.publisher(for: NSNotification.Name.AVPlayerItemDidPlayToEndTime)

We will use onReceive observer to receive the notification to reset player’s seek time to zero and play the video again.

struct DevTechieVideoPlayerExample: View {
    let player = AVPlayer(url: URL(string: "https://www.pexels.com/video/8759455/download/")!)
    let videoEnded = NotificationCenter.default.publisher(for: NSNotification.Name.AVPlayerItemDidPlayToEndTime)
    var body: some View {
        VStack {
            Text("DevTechie")
                .font(.title3)
            VideoPlayer(player: player)
        }
        .onAppear {
            player.play()
        }
        .onReceive(videoEnded) { _ in
            player.seek(to: .zero)
            player.play()
        }
    }
}