Search code examples
swiftswiftuivideo-player

SwiftUI Video player how to set url


I am trying to create a view that allows me to send a URL of the video I want to play. My view looks like this:

import SwiftUI
import AVKit

struct PlayerView: View {
    var videoURL : String
    private let player = AVPlayer(url: URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!)

var body: some View {
    VideoPlayer(player: player)
        .onAppear() {
            // Start the player going, otherwise controls don't appear
            player.play()
        }
        .onDisappear() {
            // Stop the player when the view disappears
            player.pause()
        }
    }
}

If try to set the URL to the passed parameter I get this error:

Cannot use instance member 'videoURL' within property initializer; property initializers run     before 'self' is available

How do I pass aURL to the View to play different movies?


I changed my code as suggested and the video does not play:

import SwiftUI
import AVKit

struct PlayerView: View {
    var videoURL : String
    @State private var player : AVPlayer?

var body: some View {
    VideoPlayer(player: player)
        .onAppear() {
            // Start the player going, otherwise controls don't appear
            guard let url = URL(string: videoURL) else {
                return
            }
            print(url)
            let player = AVPlayer(url: url)
            self.player = player
            self.player?.seek(to: CMTime.zero)
            self.player!.play()
        }
        .onDisappear() {
            // Stop the player when the view disappears
            player?.pause()
        } 
    }
}

I am trying to change the video on the timer. I tried this:

} else {
    VideoPlayer(player: viewModel.player)
    .onAppear {
          viewModel.urlString = "https://testsite.neumont.edu/images/green.mp4"

                        }

    .onReceive(movieSwitchTimer) { _ in
       DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
      viewModel.urlString = "https://testsite.neumont.edu/images/earth.mp4"
                            }
    self.activeImageIndex = (self.activeImageIndex + 1) % slideStore.images.count
         }
    }

Solution

  • There are a number of ways to achieve this, but since you're already using onAppear, that seems like a good place to accomplish this:

    struct PlayerView: View {
        var videoURL : String
        @State private var player : AVPlayer?
        
        var body: some View {
            VideoPlayer(player: player)
                .onAppear() {
                    // Start the player going, otherwise controls don't appear
                    guard let url = URL(string: videoURL) else {
                        return
                    }
                    let player = AVPlayer(url: url)
                    self.player = player
                    player.play()
                }
                .onDisappear() {
                    // Stop the player when the view disappears
                    player?.pause()
                }
        }
    }
    

    You'll want player to be @State because it should persist between renders of the view.

    Update, based on comments, handling a scenario where the URL may change:

    class VideoViewModel : ObservableObject {
        @Published var urlString : String? {
            didSet {
                guard let urlString = urlString, let url = URL(string: urlString) else {
                    return
                }
                player = AVPlayer(url: url)
                player.seek(to: .zero)
                player.play()
            }
        }
        var player = AVPlayer()
    }
    
    struct ContentView : View {
        @StateObject var viewModel = VideoViewModel()
        
        var body: some View {
            VideoPlayer(player: viewModel.player)
                .onAppear {
                    viewModel.urlString = "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
                    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
                        viewModel.urlString = Bundle.main.url(forResource: "IMG_0226", withExtension: "mp4")!.absoluteString
                    }
                }
        }
    }
    

    In the update, you can see that I store the player in an ObservableObject. Any time the url string gets changed, it reloads the player. The onAppear is not necessary in your code -- it's just a way to show loading different videos at different times (the first being the URL you provided, the second being a URL from my bundle).