While I work with them every day, I never really understood Xcode 4 schemes until recently.1
What changed was that I listened to Apple’s WWDC 2012 session 408 “Working with Schemes and Projects in Xcode”, presented in part by the esteemed @rballard, which explains them very well and in great detail.
Now, I can’t give you a link to this session, because it’s hidden behind an ADC login. You have to go to https://developer.apple.com/videos/wwdc/2012/, sign in, and search for the title. This, for me, is an argument for Apple to release its WWDC sessions to the public, at least the older ones, with permanent links and searchable keywords, because this one session alone would banish the frustration and confusion of many developers wrestling with Xcode 4.
In Xcode 3, in general, you had a combination of simple actions (the Build action, the Run action), and build configurations (the Debug build configuration, the Release build configuration). You couldn’t make new actions, you could only make new build configurations — useful if you wanted, say, to make two different kind of release builds. You’d switch to the desired configuration, and then perform the action.
In Xcode 4, you can still make new build configurations if you wish (Project editor view → project → Info tab), but you can’t switch between them directly from the UI like you could in Xcode 3. Instead, you switch between schemes.
Schemes are built on top of all the older infrastructure: projects, targets, and build configurations. They can’t work without it, but they’re really an entirely different beast.
Every scheme has five predefined actions: Run, Test, Profile, Analyze, and Archive. Make a new scheme? Get five actions. Make a second scheme? Get five more actions.
More accurately, you get a bunch of settings for those five actions. To see them for yourself, choose your scheme name and Edit Scheme… from the Xcode 4 toolbar, like so:
and you’ll see the scheme-editing sheet. There, select Run:
The first setting? The build configuration to use. That’s how the old and the new are tied together. The Run, Test, and Analyze actions by default use the Debug configuration, while Profile and Archive use the Release configuration. I can see the logic in it, but it takes a little while to get your head around it. Xcode 4 is saying, “We don’t expect you to switch back and forth between build configurations. Just use the right action for the job, and we’ll take care of which build configuration to use for you.” (Well, if it could talk.2)
Hey, hey, hey: didn’t you forget something? What about building?
Turns out, building isn’t one of the scheme actions. That’s because it’s a prerequisite of each of the five actions. Want to run? Gotta build first. Want to Analyze? Gotta build first.
One neat thing about schemes is that, for each action, you can choose what to build:
When you first create an app project, and you configure Xcode to create a test target in addition to an app target, it sets up your default app scheme such that, for the Test action, it builds both your test target and your app target. You don’t need an extra test target scheme.
(I actually go further, and check the test target checkbox for the Run scheme action too, so I know immediately if code changes break my test build.)
But for every target you create after that, by default Xcode auto-creates a new, separate scheme for it. That…works, I guess, but I prefer to take advantage of the configurable build stage to ensure I only need one scheme per project.
Let’s say, in my sample project, I make a new MyLibrary target. This creates a MyLibrary scheme, which I don’t want. So I go to manage my schemes:
And I delete the MyLibrary scheme using the minus button:
(Note: this scheme-managing sheet is also where you can turn off the preference to auto-generate schemes.)
Then I go back to the build view of my remaining scheme and add in the MyLibrary target using the plus button:
Now, a library isn’t the best example here, because you normally want to set a build dependency between the library target and the app target, and that dependency will ensure the library gets built regardless of whether it’s explicitly part of the scheme. A better example would be an entirely separate binary, like a plugin or a command-line tool. But I think you get the picture.
Two more points:
First, things get more complicated if you want additional build configurations, such as a “Debug with Special Profiling” or “Release for Beta-Testers”. Because the build configurations are built into the scheme actions, if you want to switch build configurations, you either need to hand-edit your scheme each time before you build (blech) or make a new scheme for the new build configuration — and get your five new actions, even if, for example, you only need to duplicate the Run action, or the Archive action. Neither solution is ideal.
My opinion is, if you’re going to bake build configurations into actions, allow users to create new custom actions for a scheme.
Second, have a look at the Xcode 4 Product menu:
You’ll see menu items for five scheme actions, Run, Test, Profile, Analyze, and Archive, at the top. Good so far.
But there’s also a Build menu item. What’s up with that?
And even more curious, there are also separate “Build” menu items for almost all the actions in their own submenu:
and “Without Building” menu items for some of the actions in their own submenu:
So even though conceptually a scheme fuses a build pre-action with an action — that’s their whole point — Xcode 4 pretty much gives you every opportunity to invoke those parts of a scheme separately.
The fact that Xcode provides this much flexibility is very good, but I have to think — if breaking with the original concept is so important that there are eight menu items to work around it…perhaps the original concept needs some tweaking?
(Also, if you were wondering: the Build menu item is the same as “Build for Running”.)
1. Which is unfortunate, because I was working on Xcode (albeit a different part of it) when they were invented.
2. Which it can: http://www.textfromxcode.com/