Bookmarks question

I’m working with bookmarks for the first time and wonder if someone can help me understand the role of relative URL’s when creating Security Scoped Bookmarks. The code snippet below is meant to access a file outside the sandbox that needs to be copied by my app.

dim relativeURL as FolderItem = fileToArchive
dim isStale as Boolean
dim options as integer = CFBookmarkMBS.kResolutionWithSecurityScope
dim url as CFURLMBS = CFBookmarkMBS.ResolveBookmarkDataToCFURLMBS(bookmarkData, options, relativeURL, isStale)

// start access
if url <> nil then
  if CFBookmarkMBS.StartAccessingSecurityScopedResource(url) then
    f = url.file
/// do the file copying here
CFBookmarkMBS.StopAccessingSecurityScopedResource(url)
  end if

I’ve successfully created a bookmark of the volume containing the file to be copied and am using it in the variable “bookmarkData”. After reading a lot of documentation, my understanding of the 4th line of code is that the variable, relativeURL, should be set to the file I want to copy so that a CFURLMBS (variable “url”) to it will be created when line 4 runs. But it doesn’t work, it always results in a nil “url” variable. The only way I get a good “url” to the file I want to copy is to set the variable “relativeURL” to “nil” and use a bookmark of its parent directory as the “bookmarkData” variable. Then line 4 runs successfully. I can then change line 7 to “f = url file.child(filename)” and get access to the file and copy it. But I can’t use that technique in the app since there is a long list of files to copy from different volume locations and I don’t want to keep asking the user for permission to access every parent folder.

Any help in getting this strategy to work, or suggestions for different strategies are greatly appreciated. As I said, this is my first run at using SSB’s and I’m sure I’ve made some basic mistakes.

I set the relative url to nil:

[code]'get data from preferences
if fieldName = “” then Return Nil
if not CheckForPrefs then Return Nil
if not theCFPrefs.AppSynchronize(theCFPrefs.kCFPreferencesCurrentApplication) Then
globals.theErrorLog.DialogErrorProceed(kErrorSynchronize)
end if
Dim theCFObject As CFObjectMBS = theCFPrefs.CopyAppValue(NewCFStringMBS(fieldName), theCFPrefs.kCFPreferencesCurrentApplication)
if theCFObject = Nil then Return nil

'make bookmark data
dim BookmarkData as string
if theCFObject.TypeDescription = “CFString” then BookmarkData = DecodeBase64(CFStringMBS(theCFObject).str)
if BookmarkData = “” then Return nil’no data

'resolve bookmark
dim relativeURL as CFURLMBS = nil
dim isStale as Boolean = false
dim options as integer = CFBookmarkMBS.kResolutionWithoutUIMask + CFBookmarkMBS.kResolutionWithSecurityScope
dim url as CFURLMBS = CFBookmarkMBS.ResolveBookmarkDataToCFURLMBS(BookmarkData, options, relativeURL, isStale)
dim error as CFErrorMBS = CFBookmarkMBS.LastError
if error <> nil then Return Nil
if url = Nil then Return Nil

'make file from bookmark
if CFBookmarkMBS.StartAccessingSecurityScopedResource(url) then
dim file as FolderItem = url.file
CFBookmarkMBS.StopAccessingSecurityScopedResource url
Return file
end if[/code]

Hello Beatrice. Thank you for your reply and confirmation that your bookmarks are resolved successfully when relativeURL is set to nil. The tricky part for me is getting the CFURLMBS (variable url) to drill down to the file I want to copy. When created, “url” contains the URLpath of a volume, for example, file:///Volumes/MyMedia. The file I want to access is somewhere inside of the “MyMedia” volume. I’ve tried using CFURLMBS.AppendPathComponent to add the relative path of the file to the “url” variable without success. I don’t think I have the right strategy yet to reach the file. I know it can be done since I have at least two apps that work this way, that is, they ask for the user’s blanket permission to access a low level folder or volume which gives then grants the app access to the files and folders inside. In fact, I noticed Final Cut Pro X asks for volume access permission when first launched under Catalina or anytime a new storage drive is mounted. I have another app that consolidates audio files using the same access technique. I want the same kind of access to the media files my app needs to copy but I haven’t been able to figure out how to do it.

@AnthonyBlack — relativeURL should never be set to the file you want to access because relativeURL needs to be a folder.

For the Catalina “security” you don’t have to do anything except to check if the file is nil or not.

Give us a concrete example for a file you want to save/restore. My example - straight out of the MBS examples - doesn’t really do more than restore a simple file.

Thank you, Beatrice and Stphane, I was able to get SSB’s working in my app. There are still some issues that Sam Rowlands has warned me about, but I’m hoping to work through those, too. I found it hard to find much detailed documentation or coding examples on SSB’s so, when it’s all buttoned-up, I’ll post my code in hopes it helps someone else.
If it were not for the coders on this forum, I would not have been able to do this. Many thanks.

Forum for Xojo Programming Language and IDE. Copyright © 2021 Xojo, Inc.