How Swift Playgrounds Work, Part III
In my last post, I showed what I found in the small “stub” app that Xcode runs when it executes a playground. It ended with the open question of how playground code is actually being executed if the stub app in the simulator doesn’t contain any of it. I spent a fair bit of time digging around inside Xcode, and there’s a lot of interesting stuff in there. It’s worth taking a look sometime if you’re curious. For now, I’m going to cut to the chase.
There’s a plugin bundle called IDEPlaygroundExecution.xcplugin
inside of Xcode that sounded exactly like what I was looking for. When I opened it in Hopper I found… exactly what I was looking for! Here’s how playgrounds are run at a high level in Xcode 9:
- Xcode makes a “stub” app, that’s the one we looked at before
- It installs it in a simulator
- It launches the app
- It attaches LLDB to the app 🤔
- It sets a breakpoint on the
executePlayground
method ‼️ - It evaluates the playground code with LLDB 😮💥
- If there’s a live view it’s shown on screen
- The app continues to run past the breakpoint, indefinitely if there’s a live view, or it’ll exit shortly
It was step 6 where I had a real good a-ha moment. A debugger already has support for evaluating top-level code, which is something you can’t have in a normal Swift file and makes Playgrounds easy to get started with. Later I realized that this is probably why executePlayground
is a no-op. It’s just a symbol to create a breakpoint on!
Now, I didn’t know this at the time, but LLDB’s connection to playgrounds is a bit of an open secret. There’s hints on this page and a few more in the Swift LLDB repo, although Xcode’s functionality is all private. There’s probably more out there.
At this point in my investigation I started thinking about how it would be possible to replicate this functionality. I’m omitting a lot of details, but two major pieces would be controlling simulators and the debugger. Well, Facebook has been working on a library and tool called FBSimulatorControl for a few years, which provides access to the private CoreSimulator framework that Xcode uses. ☑️ LLDB has a C++ API, and there are a few pre-existing Swift wrappers for it. ☑️
I’ve glossed over some of the finer points of Swift playgrounds for now, but I was beaming once I figured out how they were executed to show live views. Then I was even more excited knowing that the most important parts of how this works should be able to be replicated by anyone, and I started planning how to do that.