Xojo Blog Create a “Recent Items” Menu

Two things about that post on TOF

  1. to me it seems overly convoluted for what it actually does
  2. I don’t see how it circumvents the security requirements in order to actually be a Recent File menu

It won’t work for the  App Store, that’s for sure.

1 Like

Thats exactly what I thought… meaning that Blog did a huge disservice to the community, because it infers it solves the Recent Item problem, and it doesn’t even come close

Arent recent items supposed to be handled with Security Scoped Bodgemarks on macOS ?

yeah that is why I think that post was a disservice… it infers that it contains the solution when in fact it doesn’t

NOOOOOOOOO!!!

I don’t know where this came from (quickly scores the web to make sure I didn’t start it many years ago), but it needs to end and soon.

AppKit has API for handling recent items, which is the recommended way to do it.

In all fairness, I can understand where their solution comes from. If you build a solution that works for the  App Store, it won’t work for Windows or Linux.

it could with a lot of #if

Well, I’ve had issues with the author on other Xojo blog posts for many of the same reasons. Overcomplicated, isn’t complete, and doesn’t acknowledge the limitations of the given code. It seems that no one reviews the articles before they’re posted.

1 Like

That is sad.

Or its provenance if copied from an open source.

Pardon my ignorance, but why? Because it writes to ApplicationData? Surely that’s where prefs and so forth are supposed to go…

App store builds must be sandboxed. When sandboxed, you aren’t able to acquire a FolderItem object for any random path, only items provided via user interaction (Drag & Drop, Open/Save). So for subsequent launches, you’re unable to acquire a FolderItem for a path you were given in a previous session and saved for later.

The original workaround for this I thought was security bodged scopemarks, but I’ll be checking out AppKit next time I have a Mac only app that needs a Recent Items menu.

I for one would love to see an example of the proper way (macOS) to handle this,

Xojo code ? Swift code ?
I’d have guessed swift would make this fairly easy

From App Sandbox in Depth

ah stephane mons posted some on TOF

Sam could give a better indicator if this is even close to workable

Public Function GetBookmarkData(extends f as FolderItem, relativeTo as FolderItem = nil, Mode as Integer = 0) as String
//# Mimicks (better) FolderItem.GetSaveInfo with modern system calls. Mode is actually not used. The bookmark will be relative if and only if you set a RelativeTo FolderItem.

declare function NSClassFromString Lib CocoaLib ( aClassName as CFStringRef ) As Ptr
declare function URLWithString lib CocoaLib selector “URLWithString:” ( cls as Ptr, URLString as CFStringRef ) as Ptr
declare function BookmarkDataWithOptions lib CocoaLib selector “bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:” (id as Ptr, options as Integer, rsrc as Ptr, relativeTo as Ptr, byref error as Ptr) as Ptr
declare function DataLength lib CocoaLib selector “length” (id as Ptr) as integer
declare sub DataBytes lib CocoaLib selector “getBytes:length:” (id as Ptr, dest as Ptr, length as integer)

const CocoaLib = “Cocoa.framework”

dim url as Ptr = URLWithString( NSClassFromString( “NSURL” ), f.URLPath )
dim relativeNSURL as Ptr
dim err as Ptr

if relativeTo<>nil then
relativeNSURL = URLWithString( NSClassFromString( “NSURL” ), relativeTo.URLPath )
end if

dim data as Ptr = BookmarkDataWithOptions( url, 0, nil, relativeNSURL, err )

if data<>nil then
dim L as integer = DataLength( data )
dim mb as new MemoryBlock( L )
DataBytes data, mb, L
return mb.StringValue( 0, L )
end if

End Function
Public Function ResolveBookmark(data as String, relativeTo as FolderItem = nil) as FolderItem
//# Resolves a bookmark and returns the corresponding FolderItem. If you created a relative Bookmark, you MUST provide the same RelativeTo parameter as during creation.

declare function NSClassFromString Lib CocoaLib ( aClassName as CFStringRef ) As Ptr
declare function URLWithString lib CocoaLib selector “URLWithString:” ( cls as Ptr, URLString as CFStringRef ) as Ptr
declare function URLByResolvingBookmarkData lib CocoaLib selector “URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error:” (cls as Ptr, data as Ptr, options as integer, relativeto as Ptr, byref stale as Boolean, byref err as Ptr) as Ptr
declare function DataWithBytes lib CocoaLib selector “dataWithBytes:length:” (cls as Ptr, bytes as Ptr, length as integer) as Ptr
declare function absoluteString lib CocoaLib selector “absoluteString” (id as Ptr) as CFStringRef

dim url as Ptr
dim relativeNSURL as Ptr
dim stale as boolean
dim err as Ptr

if relativeTo<>nil then
relativeNSURL = URLWithString( NSClassFromString( “NSURL” ), relativeTo.URLPath )
end if

dim mb as MemoryBlock = data
dim nsdata as Ptr = DataWithBytes( NSClassFromString( “NSData” ), mb, mb.size )

url = URLByResolvingBookmarkData( NSClassFromString( “NSURL” ), nsdata, 0, relativeNSURL, stale, err )

if url<>nil then
return new FolderItem( absoluteString( url ), FolderItem.PathTypeURL )
end if

End Function

That’s crazy, where are you supposed to put prefs without asking the user to select a prefs folder with every launch?

Wait - are we talking about the same thing? Xojo’s SpecialFolder.ApplicationSupport return the user’s ApplicationSupport folder, not the system’s.

prefs are not the same as recent items
Those you can do exactly as you expect and save in appdate etc

But recent items require access to the file system and, for security purposes, require use interaction to grant permissions OR you hang on to them in a way that you can remember they HAD given permissions - via security scoped bookmarks

So the implementation on the blog will NOT work in an app from the App store
Simply because it does it WRONG for an app from the App store

There are a few select “safe areas” you are allowed to randomly access, one of which is SpecialFolder.ApplicationData.Child(“yourapp”). You can store settings and related things there.

The issue comes into play when you try to re-construct a FolderItem for say /Users/tim/Desktop/My Last Document.rbx while sandboxed. I don’t quite recall exactly how it fails, with a nil instance or a garbage instance where Exists=false; but you will not get access to the item due to the sandboxing.

In my experience, the “Safe Areas” are:

SpecialFolder.ApplicationData.Child("com.your.bundle.identifier")
SpecialFolder.SharedDocuments.Child("com.your.bundle.identifier") // (this is /Users/Shared/)
SpecialFolder.Temporary

However Sam has said he’s experienced issues with SpecialFolder.Temporary. I have not experienced these same issues, but it would not be wise to dismiss these warnings.

Edit: Update/removed a tilde that was not correct.