swiftmacosavplayersecurity-scoped-bookmarks

Security Scoped Bookmark - bookmark resolves but still can't access the file


EDIT: Additional information added at the bottom

I have a sandboxed, document based application that loads a user selected quicktime movie into an AVPlayer, and everything was working perfectly.

Now I am upgrading the code so that it will use Security Scoped bookmarks to get the URL rather than just storing a URL string so that the persistent store will allow the movie to be loaded upon relaunch of the application. When the bookmark is created it is stored in a Data variable of a managed object.

For some reason, this has broken the AVPlayer. While I have created a bookmark from the user selected URL, and can resolving the URL from the bookmark when the application is relaunched, the movie is not getting loaded into the AVPlayer correctly and I can't figure out why... I have confirmed that the URL being resolved from the bookmark does point to the movie file.

I have also added the appropriate entitlements to the project.

Here is my code:

Function Where User Selects a Movie To Load and Bookmark is Created

 @IBAction func loadMovie(_ sender: Any) {

    let openPanel = NSOpenPanel()
    openPanel.title = "Select Video File To Import"
    openPanel.allowedFileTypes = ["mov", "avi", "mp4"]

    openPanel.begin { (result: NSApplication.ModalResponse) -> Void in
        if result == NSApplication.ModalResponse.OK {

            self.movieURL = openPanel.url
            self.player = AVPlayer.init(url: self.movieURL!)

            self.setupMovie()

            if self.loadedMovieDatabase.count > 0 {
                print("Movie Object Exists. Adding URL String")
                self.loadedMovieDatabase[0].urlString = String(describing: self.movieURL!)
            } else {
                print("No Movie Object Exists Yet.  Creating one and adding URL String")
                let document = NSDocumentController.shared.currentDocument as! NSPersistentDocument
                let myManagedObjectContext = document.managedObjectContext!
                let newMovie = NSEntityDescription.insertNewObject(forEntityName: "Movie", into: myManagedObjectContext) as! MovieMO
                self.loadedMovieDatabase.append(newMovie)
                self.loadedMovieDatabase[0].urlString = String(describing: self.movieURL!)
            }

            // create Security-Scoped bookmark - Added 2/1/18
            do {
                try self.loadedMovieDatabase[0].bookmark = (self.movieURL?.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil))!
            } catch {
                print("Can't create security bookmark!")
            }

        }
    }

}

Function where Bookmark is Resolved into URL and Movie is Loaded

       // initialize AVPlayer with URL stored in coreData movie object if it exists and is a valid path
    if loadedMovieDatabase.count > 0 {
        // initialize with saved movie path if it is valid (from security bookmark data)
        // let myURL = URL(string: loadedMovieDatabase[0].urlString!) <- replaced with new code below
        print("Loading URL from Bookmark")
        var urlResult = false
        var myURL : URL
        do {
            try myURL = URL.init(resolvingBookmarkData: loadedMovieDatabase[0].bookmark, bookmarkDataIsStale: &urlResult)!
            print("URL Loaded from Bookmark")
            print("URL is", myURL)
            let isSecuredURL = myURL.startAccessingSecurityScopedResource()
            print("IsSecured = ", isSecuredURL)
            player = AVPlayer.init(url: myURL)
            print("Setting Up Movie")
            setupMovie()
        } catch {
            // No Data in bookmark so load default ColorBars movie instead
            print("No Security Bookmark Available. Reverting to Default Color Bars")
            let myURL = URL(string: initialMoviePath)
            player = AVPlayer.init(url: myURL!)
            setupMovie()
        }
    } else {
        // load default ColorBars movie instead
        print("Nothing was loaded so just set up a new document.")
        let myURL = URL(string: initialMoviePath)
        player = AVPlayer.init(url: myURL!)
        setupMovie()
    }

I am new to Security-Scoped Bookmarks, so I'm hoping that this may be obvious to anyone who has worked with them before.

I'm wondering if it's a problem with:

let isSecuredURL = myURL.startAccessingSecurityScopedResource()

Perhaps I'm calling this incorrectly? Sometimes I find Apple's documentation to be vague and confusing... Any insight would be appreciated!

EDIT:

I believe I know why this is happening, but I'm not sure how to fix it...

myURL.startAccessingSecurityScopedResource()

always returns FALSE... per the documentation that would mean that it's not working. Additionally, while the movie file is located on my Desktop, the Resolved URL comes up as the following (this may be normal, I don't know.):

file:///Users/me/Library/Containers/myapp/Data/Desktop/sample_on_desktop.mov

The apple docs make reference to the fact that a Document Scope can not use files in the system (aka "/Library"), but my entitlements are setup to use application-scope bookmarks, and my bookmark was created using the nil flag for relativeURL: so this shouldn't be an issue.


Solution

  • I just stumbled upon the answer accidentally...

    For starters, when I was resolving the URL, I was not using the method which allows you to include OPTIONS, so my URL was resolved WITHOUT the security-scope. My original code to resolve was:

    try myURL = URL.init(resolvingBookmarkData: loadedMovieDatabase[0].bookmark, bookmarkDataIsStable: &urlResult)!
    

    When I should have been using the version with options here:

    try myURL = URL.init(resolvingBookmarkData: loadedMovieDatabase[0].bookmark, Options: URL.bookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStable: &urlResult)!
    

    Basically, I used the first init option Xcode presented in the predictive list with the words "resolvingBookmarkData:" when I should have looked further down the list. (This is how I found my error.)

    NOTE also that it's important to use...

    URL.bookmarkResolutionOptions.withSecurityScope
    

    and not

    URL.bookmarkCreationOptions.withSecurityScope
    

    ...when you're resolving your URL or it doesn't appear to work correctly.

    Thus ends my frustration with this problem :) I hope this explanation might help others facing this problem!