3

I am trying to create a simple SwiftUI application using XCode 11.5 on macOS 10.15.4 using AVKit/AVFoundation to play videos stored on the local file system. I would like to be able to select the movie based on a file path determined by the user's actions (e.g. selecting from a series of menu options).

I am working from a code snippet found here: https://qiita.com/croquette0212/items/eb97e970d1dcfa2932fd

import SwiftUI
import AVKit
import AVFoundation

class VideoItem: ObservableObject {
    @Published var player: AVPlayer = AVPlayer()
    @Published var playerItem: AVPlayerItem?

    func open(_ url: URL) {
        let asset = AVAsset(url: url)
        let playerItem = AVPlayerItem(asset: asset)
        self.playerItem = playerItem
        player.replaceCurrentItem(with: playerItem)
    }
}

struct PlayerView: NSViewRepresentable {
    @Binding var player: AVPlayer

    func updateNSView(_ NSView: NSView, context: NSViewRepresentableContext<PlayerView>) {
        guard let view = NSView as? AVPlayerView else {
            debugPrint("unexpected view")
            return
        }

        view.player = player
    }

    func makeNSView(context: Context) -> NSView {
        return AVPlayerView(frame: .zero)
    }
}


struct ContentView: View {
    @ObservedObject var videoItem: VideoItem = VideoItem()

    var body: some View {
        VStack {
            if videoItem.playerItem != nil {
                PlayerView(player: $videoItem.player)
            }
            Button(action: {
                let panel = NSOpenPanel()
                panel.allowedFileTypes = [kUTTypeMovie as String]
                DispatchQueue.main.async {
                    if panel.runModal() == .OK {
                        guard let url = panel.url else {
                            return
                        }
                        self.videoItem.open(url)
                    }
                }
            }) {
                Text("Open")
            }
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

This works perfectly well for selecting a file using the "panel" object, but I can't figure out the simple change of passing my own value of "url" to videoItem.open().

I have tried replacing the block:

let panel = NSOpenPanel()
panel.allowedFileTypes = [kUTTypeMovie as String]
DispatchQueue.main.async {
    if panel.runModal() == .OK {
        guard let url = panel.url else {
           return
        }
        self.videoItem.open(url)
    }
}

, defining my value of "url" using URL(string:) or Bundle.main.url(forResource:, withExtension: ) or other options without any success. The application compiles and runs without error, but it does not display the video, only a placeholder image indicating that the file was not found.

I don't understand what the difference is between the value of "url" returned from panel.url and the values that I am manually assigning. I've checked the contents of panel.url using .absoluteString and this matches exactly the values which I am manually assigning.

I am surely missing something very obvious but I can't see what it is. Please, can anybody provide some help?

crispedman
  • 71
  • 7

1 Answers1

3

Simple solution to this problem: make sure that the video file is being bundled with the target application.

I thought I was doing so, but apparently not, and a way to check (XCode 11) is:

  • click video file in the Project Navigator window and then,

  • tick the Target Membership box next to the target App in the File
    Inspector window

That's it. Credit for this answer belongs to https://github.com/SergioEstevao/ who has a AVPlayer solution for SwiftUI applications: https://github.com/SergioEstevao/SVEVideoUI that does exactly what I was looking for.

crispedman
  • 71
  • 7
  • 1
    Go ahead and accept your own answer. It’s happened to all of us. There’s a lot of moving parts to a working app! Glad you figured it out. – Mozahler Jun 01 '20 at 22:06