Today is a big day. I gathered up the courage necessary to publish my first code on GitHub for everybody to see (and criticize).
The origin story
As a MacAdmin, I’ve had the need to set the favorite items presented in the Finder sidebar. In the past, I could do that with a tool called mysides. Unfortunately, this tool uses deprecated functions that apparently do not work anymore on macOS 15+.
In the meantime I found a Python script that could perform the same task: FinderSidebarEditor. That script uses PyObjC to access the same deprecated functions that mysides used, but strangely, they still seem to work. But that is definitely not a safe choice for the future, and being a script, it is quite slow.
Frustrated by the whole situation and the lack of support by Apple to provide us with a trusted and official way to perform these tasks, I browsed the internet until I found a very interesting, although all in Japanese, website that appeared to do exactly what I needed. Aside from the fact that it was in Japanese, it presented a code in AppleScript that seemed to do the trick.
I do not read Japanese, but I kind of can read AppleScript, so I tried to figure out what that script was doing.
Since macOS 13 Ventura, the sidebar items are stored in an .sfl3 file, located in Library → Application Support → com.apple.sharedfilelist. The favorite items (the upper part of the sidebar) are in a file called com.apple.LSSharedFileList.FavoriteItems.sfl3, while the volume items (second part of the sidebar) are in com.apple.LSSharedFileList.FavoriteVolumes.sfl3.
These sfl3 files are plist-type files but are archived, which means that displaying them with a command line tool like plutil or PlistBuddy, or with a GUI tool like Plist Edit Pro will result in something very hard to read.
For example, here is an excerpt of the output from a com.apple.LSSharedFileList.FavoriteItems.sfl3 file with plutil:
{
"$archiver" => "NSKeyedArchiver"
"$objects" => [
0 => "$null"
1 => {
"$class" => <CFKeyedArchiverUID 0x60000175c780 [0x200b83ef8]>{value = 12}
"NS.keys" => [
0 => <CFKeyedArchiverUID 0x60000175c700 [0x200b83ef8]>{value = 2}
1 => <CFKeyedArchiverUID 0x60000175c720 [0x200b83ef8]>{value = 3}
]
"NS.objects" => [
0 => <CFKeyedArchiverUID 0x60000175c740 [0x200b83ef8]>{value = 4}
1 => <CFKeyedArchiverUID 0x60000175c760 [0x200b83ef8]>{value = 28}
]
}
2 => "items"
3 => "properties"
4 => {
"$class" => <CFKeyedArchiverUID 0x60000175c8a0 [0x200b83ef8]>{value = 27}
"NS.objects" => [
0 => <CFKeyedArchiverUID 0x60000175c800 [0x200b83ef8]>{value = 5}
1 => <CFKeyedArchiverUID 0x60000175c820 [0x200b83ef8]>{value = 15}
2 => <CFKeyedArchiverUID 0x60000175c840 [0x200b83ef8]>{value = 18}
3 => <CFKeyedArchiverUID 0x60000175c860 [0x200b83ef8]>{value = 21}
4 => <CFKeyedArchiverUID 0x60000175c880 [0x200b83ef8]>{value = 24}
]
}
5 => {
"$class" => <CFKeyedArchiverUID 0x60000175c780 [0x200b83ef8]>{value = 12}
"NS.keys" => [
0 => <CFKeyedArchiverUID 0x60000175c8c0 [0x200b83ef8]>{value = 6}
1 => <CFKeyedArchiverUID 0x60000175c8e0 [0x200b83ef8]>{value = 7}
2 => <CFKeyedArchiverUID 0x60000175c900 [0x200b83ef8]>{value = 8}
3 => <CFKeyedArchiverUID 0x60000175c920 [0x200b83ef8]>{value = 9}
]
"NS.objects" => [
0 => <CFKeyedArchiverUID 0x60000175c940 [0x200b83ef8]>{value = 10}
1 => <CFKeyedArchiverUID 0x60000175c960 [0x200b83ef8]>{value = 11}
2 => <CFKeyedArchiverUID 0x60000175c980 [0x200b83ef8]>{value = 13}
3 => <CFKeyedArchiverUID 0x60000175c9a0 [0x200b83ef8]>{value = 14}
]
}
6 => "visibility"
7 => "CustomItemProperties"
8 => "Bookmark"
9 => "uuid"
10 => 0
11 => {
"$class" => <CFKeyedArchiverUID 0x60000175c780 [0x200b83ef8]>{value = 12}
"NS.keys" => [
]
"NS.objects" => [
]
}
...
}
It looks pretty much like gibberish.
Now if we unarchive that using a simple code that calls NSKeyedUnarchiver.unarchivedObject we get something much easier to read. Here is the same file as above, but unarchived:
{
"items" => [
0 => {
"Bookmark" => {length = 536, bytes = 0x626f6f6b 18020000 00000510 40000000 ... 08010000 00000000 }
"CustomItemProperties" => {
}
"uuid" => "13C37DEF-945F-49BB-AFF6-10941C449D1B"
"visibility" => 0
}
1 => {
"Bookmark" => {length = 660, bytes = 0x626f6f6b 94020000 00000510 40000000 ... 60010000 00000000 }
"CustomItemProperties" => {
}
"uuid" => "792AB5AF-9346-4E85-AD0A-71271C72BED4"
"visibility" => 0
}
2 => {
"Bookmark" => {length = 684, bytes = 0x626f6f6b ac020000 00000510 40000000 ... 78010000 00000000 }
"CustomItemProperties" => {
}
"uuid" => "ED49F123-A32A-467D-90A8-A9714E65A0B6"
"visibility" => 0
}
3 => {
"Bookmark" => {length = 704, bytes = 0x626f6f6b c0020000 00000510 40000000 ... 8c010000 00000000 }
"CustomItemProperties" => {
}
"uuid" => "6EA42443-295F-4F80-812A-932DDE6FA73B"
"visibility" => 0
}
4 => {
"Bookmark" => {length = 952, bytes = 0x626f6f6b b8030000 00000510 40000000 ... a8020000 00000000 }
"CustomItemProperties" => {
}
"uuid" => "4C60DF60-00C4-403B-9435-8ACF3CC5FCE0"
"visibility" => 0
}
]
"properties" => {
"com.apple.LSSharedFileList.ForceTemplateIcons" => 1
}
}
Much easier to read and process ! This is an array of bookmarks (an object that refers to a file, kind of like an alias) along with some properties.
So in summary, the process the manipulate these files is simple:
- unarchive the slf3 file
- retrieve and modify the items array
- re-archive into an sfl3 file
This is basically what the Applescript from Quicktimer was doing. I translated all of that into Swift, added more options (remove, list, removeall) and turned it into a shell command with arguments.
This is how sbedit was born.
Usage
Using sbedit is quite simple. It understands six commands:
--add add all the paths provided as arguments to the sidebar
--removeAll remove all items from the sidebar
--remove remove the item path provided as argument from the
sidebar
--list display the list of paths currently in the sidebar
--reload reloads the Finder sidebar. This command takes an
optional argument "--force". See discussion below.
--version prints the version number
A workflow would for example be:
$ sbedit --removeAll
$ sbedit --add /Applications ~/Desktop ~/Documents ~/
$ sbedit --reload
This will remove all existing items in the favorites sidebar, add Applications, Desktop, Documents and the home directory, and finally reload the service.
Reloading the interface
Those sfl3 files are handled by a process called sharedfilelistd. In order for the changes to be taken into account, that process must be restarted. This is what the –reload command does. However, it might take some time for the new settings to be visible in a Finder window. This is particularly true if a Finder window has already been opened. In that case, you can ask sbedit to kill the Finder and relaunch it, thus making the changes immediately visible. In order to do that, you simple add the --force argument to --reload:
sbedit --reload --force
In other words, if in you workflow you plan on running sbedit at login (maybe with a tool like Outset) before any Finder window is opened, then a simple --reload might be enough. Otherwise, you can use the --force argument which will create a small “flicker” of the interface.
Support for macOS Tahoe
With macOS 26, slf3 files have been replaced with slf4 files. However, the structure appears to be very similar, and sbedit can handle both.
In the code, function names and comments all mention sfl3, but depending on the version of macOS, it will either load an slf3 or an sfl4 file.
Impostor syndrom
Over the last 30 years or so, I’ve developed several tools and software, but this is the first time I’m making the code available as an open-source project. The reason for that is I am not a programmer. I’ve never taken any programming class (except for some Pascal and Fortran during my physics studies). As far as programming goes I am self-taught.
As such, my code is probably extremely ugly, lacks best practice and could be much more elegant.
But here I am, publishing my code because I believe it can be useful for others, and hopefully people will see its value behind its ugliness.
Ladies and gentlemen, here comes sbedit
Here is the link to the project page:
Any comments, suggestions and feedback will be greatly appreciated !