Two things about that post on TOF
- to me it seems overly convoluted for what it actually does
- I don’t see how it circumvents the security requirements in order to actually be a Recent File menu
Two things about that post on TOF
It won’t work for the App Store, that’s for sure.
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
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.
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.