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:

  1. Xcode makes a “stub” app, that’s the one we looked at before
  2. It installs it in a simulator
  3. It launches the app
  4. It attaches LLDB to the app 🤔
  5. It sets a breakpoint on the executePlayground method ‼️
  6. It evaluates the playground code with LLDB 😮💥
  7. If there’s a live view it’s shown on screen
  8. 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.