MacOS: open local SPM Packages in Xcode by double-clicking them (using xattr)

If anyone here has wanted the option to open a local SPM folder in Xcode by double-clicking on the folder, here's a way to do that.

You can use the Mac's "bundle bit" and "LaunchServices.OpenWith" extended attributes. I don't know how many future versions of MacOS will continue to support this. These days, Apple prefers setting metadata by file-extension.

The trade-off is that to view the Package's contents in Finder, you need to right-click it and select "Show Package Contents"

Hope this information is welcome. Maybe I'm the only person who wants to work with Packages this way. I really don't know.

Method A (use if you need to script, or don't want to rename your Package folder)

There's two and a half steps you need to perform...

(Step 1) Turn on the Bundle Bit:

xattr -wx com.apple.FinderInfo 0000000000000000200000000000000000000000000000000000000000000000 /path/to/package

(Step 2) Tell LaunchServices that Xcode is the owner of the file (or "file-like folder" if you prefer)...

xattr -wx com.apple.LaunchServices.OpenWith 62706C6973743030D30102030405065776657273696F6E54706174685F101062756E646C656964656E74696669657210005F101C2F4170706C69636174696F6E732F58636F64652D626574612E6170705F1012636F6D2E6170706C652E64742E58636F6465080F171C2F31500000000000000101000000000000000700000000000000000000000000000065 /path/to/package

The half-step here is this: that "step 2" probably won't work for you, as is. That's because the long hex string is actually an encoded property list, which points to my version of Xcode. So you need to figure out what that should look like on your Mac.

(The Half-Step) Take some random file on your drive, like a text file. Then use Finder's "Get Info" window and set the file to "Open with Xcode." That will write an extended attribute to the file. Then you can examine that metadata, and revisit (Step 2) equipped with a hex blob that works for you

xattr -p com.apple.LaunchServices.OpenWith /path/to/xcode.txt

If you get lost, I have a project that uses code to do this in my GitHub. If you dig through my source, it should be easy to adapt it to your own.

Method B (use if you don't like using the CLI, or are fine your Package name ending in ".pkg")

(Step 1) Turn on the Bundle Bit:

In Finder, add the extension ".pkg" your Package folder

(Step 2) Tell LaunchServices that Xcode is the owner of the package...

Right-click on the package folder/bundle, and use Finder's "Get Info" window to set it to "Open with: Xcode."

If you're curious about what's in the hex blob, you can pop it into a site like this https://www.rapidtables.com/convert/number/hex-to-ascii.html

Couple questions:

Why can't you use your "half step" on the folder instead of step 2 on the folder?
Can you write a shell script that does this process in bash or in swift?
Afterwards does open folder in the Terminal work to open it in Xcode?

Are you aware that double-clicking the Package.swift file will open the entire package in Xcode (not just that file)?

1 Like

Yes, you can definitely set it directly in Finder, same as the second "method". Since the bundle bit step needs to be done via CLI unless you're okay with having all your projects named "Foo.pkg", I thought I ought to list one version that was fully scriptable.

One could fairly easily write a shell script, but it's probably just as well to use Swift? Here's the package with the relevant code from my project: https://github.com/charlesism/manifest_generator/tree/master/ExtendedAttributes

open /my/package from Terminal opens the package in Xcode. I just double-checked.

Yes. I've been organizing my projects with all my Swift files at the root of every Package. That means every time I switch to a different package, I have to hunt for the Package.swift file (which has the same icon as all the other Swift files in the folder, naturally) before I can edit that library's code.

Also, I'm using a lot of nested Umbrella projects, so switching between opaque bundles and folders can be convenient for focusing on one package at a time.