W.D. Neumann's Thing

You have to pay the SyntaxMan

So I'm about to begin work on my Swift representer for the v3 release of Exercism, and I'm pretty sure the easiest way to get that working is using SwiftSyntax.

As with any exploration of a new library/concept in Swift, I like to putz about in a Playground first. Unfortunately, getting external libraries to work well with Playgrounds has always been a bit of an annoyance. The Point Free guys have a great episode on making it work in their Playground Driven Development episode, though with the release of Xcode 12, it appears you don't have to go through quite so many hoops. As seen in their Explore Packages and Projects with Xcode Playgrounds presentation from WWDC 2020, you can now just add a Playground to your package and make sure the Build Active Scheme checkbox is checked in the File Inspector for your Playground (don't worry, if you don't know how to do any of this, I'l be walking through it below).

Unfortunately, if you follow this simple process with SwiftSyntax, you'll find that it doesn't work, either for the Playground (here you'll find that you can import the package and see it in your autocomplete, but if you try to actually use it you'll get the 'not in scope' errors you would see if you had never imported the package), or for the application itself (here you get a crash because Xcode couldn't find a required library).

dyld: Library not loaded: @rpath/lib_InternalSwiftSyntaxParser.dylib

None of this is really a secret, it's briefly discussed in the SwiftSyntax README, but it is discussed in brief and frankly I missed it the first 30 or so times I looked for that info. Plus, there's an easier way than hunting down lib_InternalSwiftSyntaxParser.dylib and adding it to your Build Phases settings, if you're starting from scratch (it's at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib by the way).

Enter SPM

One of the things that was really making my pull my hair out is that the test app I had written stole from the SwiftSyntax README to make sure SwiftSyntax was working correctly is that the app ran just fine when I was building it via Swift Package Manager with the swift run command. What kind of fuckery was this?

I was beginning to think the issue was my recent upgrade to Big Sur and Xcode 12.2 and that somehow broke compatibility with SwiftSyntax while a pure Swift approach still worked, but that wasn't it. As it turns out, the reason is that SPM somehow knows that it needs to link lib_InternalSwiftSyntaxParser.dylib and it does so for you while Xcode has no clue that it needs to do so. So the solution is to let SPM tell Xcode what to do. So here are the steps needed to get a Playground and a project up and running with SwiftSyntax with a minimum of fuss. This should work for any other 3rd party package that offers a Swift Package Manager package, though not all steps are necessarily necessary (in particular step 3), and I can't guarantee that they won't come with fuckery of their own that this doesn't fix. But I can say for sure that with SwiftSyntax for Swift 5.3 using Xcode 12.2 on macOS Big Sur, this does work.

The Instructions

Step 1: Initialize a package for your project

This is pretty self-explanatory. Use the SPM to initialize a project for you in a directory of your choosing. Here, I'm initializing an executable project named stax in a directory named stax. You can initialize it as a library or an empty project if that's what you want.

swift package init --type executable --name stax

Step 2: Edit your Package.swift file

Open up your Package.swift and add SwiftSyntax as a dependency. Be sure to use the correct tag for your version of Swift. At this point in time I need tag 0.50300.0. Don't forget to add "SwiftSyntax" to your target's dependencies.

Adding dependencies to Package.swift

Once you've got that set up close the Xcode window if that's what you used to edit Package.swift.

Step 3: Generate an Xcode Project File

This is the crucial step for getting SwiftSyntax to work in Xcode. It generates an xcodeproj file that tells Xcode to link the necessary library. You can skip this for a lot of packages, but it doesn't hurt to do it for all of them.

swift package generate-xcodeproj

Step 4: Add a Playground to your project

Open your newly generated xcodeproj file to bring up your project in Xcode. Right-click on your project icon and select "New Group". Name the new group what you like, I'm using "Playgrounds".

adding a new group

Next, choose File > New > Playground from the menu and create a new playground. I just put it in the new Playgrounds folder. Be sure to add it to your project.

adding a new Playground

Now you should be able to use SwiftSyntax in both your playground and your project.

Running in the Project

Running in Playground

And, of course, you can run it via SPM.

Running in Terminal

Hopefully this helps if you were confused about any of this. I know I will be in about three months.

Tagged with: