Xcode Configurations vs. Schemes
My name is John Daub, and I’m an iOS Developer at Big Nerd Ranch.
In this screencast, we’ll look at two features of Xcode that are not always well-understood, but are important if we wish to use Xcode to make our lives as a developer both easier and better.
They are configurations and schemes.
What are they?
App development is pretty complicated these days. Our apps are using technologies like Core Location. Localization Multi-threading - technologies that are complicated to work with, both at the code level and in daily development work on our devices and the simulator . We may need to build our app in different ways. Maybe we have a regular version of the app and then a free “lite” version. Maybe we need to have different builds that can point to different server environments (development, staging, production). We need to build and run with a lot of things.
How can we manage this?
What often happens is developer does a lot of extra work. They see that Xcode supports multiple targets, so they’ll create multiple targets - a target to build the app for the development environment. Then a target to build the app (again) for the staging environment. Then a target to build the same app (yet again) for the production environment. And this sort of target explosion is a common thing.
If you go down this road, you eventually discover it’s quite painful to manage and maintain. Too many build settings to keep in sync. Then when you run the app, there’s so much manual setup and tear down to make everything go.
Thankfully, Xcode provides us the tools to make this easier: configurations and schemes.
First, we need to understand some Xcode concepts.
A project is a repository of all the files and information used to build your products.
A target specifies a specific product to build. It contains the files, information, and instructions to build that single product. A project can contain multiple targets.
When a project contains multiple targets, you typically want those targets to be wholly different things. Like one target builds the app, one target builds its framework, and a third target builds the unit tests. The scenario I mentioned before: having multiple targets to build multiple flavors of the app? That’s not desirable, because each target contains a multitude of build settings that you’d have to struggle to keep in sync.
A build setting is a variable that contains information about how a particular aspect of a product’s build process should be performed. For example, where to find the Info.plist file, or how the compiler should behave. The manipulation of these build settings tends to be what provides us with the different flavors of our app.
How can we have multiple flavors of our app with a single target?
This is where build configurations come into play.
A build configuration is a named grouping of build settings that provide a particular flavor of those settings. For example, you might create a build configuration to generate debug-oriented builds, and another configuration to generate a release-oriented build. Or maybe you’ll have 3 configurations: one to build the app for the development server, one to build a flavor of the app to connect to the staging server, and a third configuration that builds the app for the production server.
Using build configurations we can easily create sets of build settings and apply them to the project as a whole, or individual targets. This then also provides a way to enable our app target to build with a particular flavor, then the framework target to also build with that same flavor -- to ensure all products that go into the app are built with the same build settings.
Build configurations help Xcode understand how to build our product, but they don’t actually build the product -- that’s where schemes come in.
You may not have actually seen schemes or really understood what they do. This is because when you create a new project in Xcode, it creates 1 scheme that’s set up with reasonable default settings, and most people have no reason to change it because things “just work”. Also, in some older projects, they had 2 schemes: one named debug and one named release -- just like the build configurations. While the schemes were schemes and the configurations were configurations, the fact they shared the same name caused a bit of blurring of the lines between the two features, and some were lead to believe they were the same thing. Either way many people’s only exposure to the notion of “scheme” was as they’d click on the popup to select a different device or simulator to run in and see the words “scheme”.
So what is a scheme?
An scheme defines on building, how and what to build. On running, how and what to run. How to test, profile, analyze, and archive. When you issue these same commands, this is what Xcode uses as the directive on what to do.
Since the most common thing we developers do in a day is build and run, let’s take a look at how schemes make building and running work.
On the Build pane, you specify what targets will build upon what command is issued. Notice here we want to always build our app, regardless of command -- which is typical. But the unit tests? We only want to build them if we’re testing, because there’s no reason to waste the time and effort to build unit tests when we’re not testing! But of course, if you wanted to build the unit tests at other times, you’re certain able to do so by checking the desired box here.
So when we want to run the app, the most important thing is to specify what build configuration we want to run. A typical thing is to run the debug configuration, since that’s most useful in our daily development life. As you can see tho, there are a lot of other things you can set that affect how your app will be run.
For example, if your app contained a French localization, to test out that localization you might go through the effort of changing the language to French on your iOS device or simulator. But this is unnecessary work (especially since you’ll likely to be frequently switching your language setting back and forth)! You can go into the Options tab of the Run pane and set your Application Language to French and viola -- the app will run in your French localization.
That’s great and all, but it’s still a lot of clicking around to enable and disable these things.
This is why Xcode supports multiple schemes.
We can go manage our schemes, then duplicate our current scheme. Let’s say we’re in the midst of creating that French localization. I can create a scheme that mimics my normal development scheme, but always launches in the French language. Now with a simple selection from the schemes popup, I can run my app in the system language or in French. That’s much more convenient -- especially for things that we developer do many times in a day.
And if you have the build configuration setup to have a configuration for development, staging, and production builds of your app, you can create schemes to make it easy to tell Xcode to build and run those flavors of your app.
There are a great many options within a scheme, and I encourage you to read the Xcode documentation to learn how they can benefit you. I wanted to point out one important checkbox: Shared.
By default, schemes are local to you. This is fine if you work alone. But if you work on a team, you probably want to share your schemes with your teammates. Granted, if you need to whip up a scheme to do something for yourself, to share is up to you. But the core schemes that make the project go, schemes that would be useful to the rest of the team -- be sure to share those.
In this screencast, we looked at Xcode’s support for build configurations and schemes. How build configurations enable us to provide the directives to Xcode on how to build our products. And how schemes then work to direct Xcode in way way to build, run, and work with our product to give us just what we need to work with. By embracing the power of configurations and schemes, we can make our daily developer life more powerful and simpler.
Once again, my name is John Daub. Thank you for watching. We’ll see you next time here soon.