giant robots smashing into other giant robots

Written by thoughtbot

gfontenot

Opinionated settings for app development in Xcode

Setting up a new Xcode project is as simple as ⇧⌘N. Unless you want to do things the right way, at which point there are a number of other configurations you need to worry about: .gitignore, .gitattributes, project level indentation settings, warning levels, etc. After doing the same setup procedure a few times, you can make it a relatively quick process. But it’s 2013, and we’re living in the future now. We have the technology to build tacocopters, but I still have to set my error levels manually? That’s just ridiculous.

Enter: liftoff.

liftoff-image

liftoff is a small Ruby gem that we’ve been working on to make new Xcode project setup as fast and painless as possible. With one simple command, you’ll have a slew of defaults set up for your project, along with some things that we think will make your life in Xcode a bit nicer.

Usage

First, install the gem, which is as simple as gem install liftoff. Then, while in your project directory (wherever you are keeping the .xcodeproj file), just run liftoff. This will set up the project defaults we like to use:

These commands can be run individually as well. For example, setting the project’s indentation level to 2 spaces can be done with liftoff indentation 2.

The best part about using liftoff over another solution like a custom project template is that liftoff can be used to quickly add these settings to an existing project without worrying about stomping on your current setup.

liftoff is currently at version 0.6 and, as usual, the code is open source on GitHub. We hope you enjoy it as much as we do.

gfontenot

Xcode Build Settings Part 1: Preprocessing

That screen. You know the one. The table of text. The one with scary sounding terms such as Mach-O Type and Rez Search Paths. The one you’re probably avoiding right now, for fear that changing one of the magic incantations will leave you with an empty husk of an app powered only by sadness and regret. There’s actually a whole ton of useful stuff which you probably didn’t know about. In this multi-part article, we’ll go over some of the fun possibilities once you understand the available settings.

Disclaimer (no seriously read this)

I know I said this wasn’t that scary. And I stick by that. But there’s one thing to be aware of. You can’t actually undo any changes you make (and might later regret). So please please please make sure you are working in a clean repo before playing around with settings you aren’t familiar with so that you can easily reset in case something blows up on you.

With that out of the way, let’s go poke it with a stick, shall we?

2001-Monolith

Compiler flags

The first thing we’re going to play around with is one of the least likely to blow up in your face. Compiler flags are used to define constants at build time, that can then be used in your code to do some tricky things to customize your code for specific build configurations.

There are 3 possible places to set these up:

  • OTHER_CFLAGS (Other C Flags)
  • GCC_PREPROCESSOR_DEFINITIONS (Preprocessor Macros)
  • INFOPLIST_PREPROCESSOR_DEFINITIONS (Info.plist Preprocessor Definitions)

The difference between the Other C Flags setting and the two Preprocessor settings is that anything you pass into Other C Flags is passed directly to the compiler as is. This means that if you want to set up a constant named FOO, you would have to format the C Flag as -DFOO. This gets passed as is to the compiler, and defines the FOO constant. However, this also means that a malformed C Flag could potentially blow up your build.

Conversely, anything passed in through one of the Preprocessor settings is passed to the compiler with -D automatically. So that same constant from before can simply be set up as FOO. This means that even if you write a malformed flag, you will only get a malformed definition in return. Because of this, I recommend sticking to the Preprocessor macros settings for defining compiler flags, leaving the Other C Flags setting for times when you actually want to pass flags directly to the compiler.

Compiler flags can be set either as a value definition (FOO=1), or a constant definition (FOO). Constant definitions are essentially boolean. They are either set, or not set. Value definitions can have value, but you really don’t want the compiler going through complex conditionals to generate your code. If you want to use a value definition, stick to setting the flag to 1 and simply checking for its existence.

So how do you use this? The most common use is dynamically swapping out sections of code based on the build configurations. Once you have your flags set up, you can do some slick dynamic compiling in your code. As an example, you could swap out an API endpoint based on the build configuration like so:

#if RELEASE
static NSString *const MY_API_URI = @"https://api.example.com/";
#else
static NSString *const MY_API_URI = @"https://api.staging-example.com/";
#endif

Remember Info.plist Preprocessor Definitions? These can be used in conjunction with the INFOPLIST_PREPROCESS (Preprocess Info.plist File) flag to do the same kind of dynamic compilation for your Info.plist file. The simplest use of this technique is to swap out your bundle identifiers and modify your product names so that you can distinguish beta builds from release builds, and be able to keep both installed at the same time. Right click on your Info.plist file, and choose Open As -> Source Code. You should now see the plist file in its raw XML. Now look for the sections you want to modify, and use the same kind of compiler conditionals used earlier.

<key>CFBundleDisplayName</key>
#if RELEASE
<string>${PRODUCT_NAME}</string>
#else
<string>${PRODUCT_NAME} Beta</string>
#endif
<key>CFBundleIdentifier</key>
#if RELEASE
<string>com.yourcompany.myapp-appstore</string>
#else
<string>com.yourcompany.myapp-beta</string>
#endif

Once you start adding compiler conditionals into your Info.plist, Xcode will start to tell you that the file has been corrupted, and is unreadable. Don’t worry, it isn’t. You can always do the same right click -> Open As -> Source Code tango you used to add the compiler conditionals in the first place. The one issue this may cause is that the “Summary” screen in newer versions of Xcode will become unable to read your Info.plist file. This means that if you want to change any of the settings contained within the file, you will have to edit the XML directly, which isn’t always the most pleasant of experiences.

Bonus round

There are actually some built in flags you can use to help compile your source code dynamically. The most interesting ones for our purposes are TARGET_IPHONE_SIMULATOR and TARGET_OS_IPHONE. These are set as value definitions, so you should be using #if to check the conditional, not #ifdef. This can be used in all sorts of interesting ways. My favorite use-case is an extension of the dynamic API constant from earlier. When developing against an API for a rails app for which I have access to the source code I prefer to run the app locally, instead of dealing with calls to a staging server. So I take the example above, and modify it like so:

#if TARGET_IPHONE_SIMULATOR
static NSString *const MY_API_URI = @"http://localhost:3000/";
#elif RELEASE
static NSString *const MY_API_URI = @"https://api.example.com/";
#else
static NSString *const MY_API_URI = @"https://api.staging-example.com/";
#endif

Now, when building for the simulator, I’m pointing the app at my local Rails server. But the app is still pointing at the real API for release builds, and the staging API for everything else. Huge time saver.

In addition, there are a number of variables you can use inside your compiler flags to create the flags dynamically. Notably, the CONFIGURATION variable corresponds to the build configuration name. That means that setting up a preprocessor macro with CONFIGURATION_$(CONFIGURATION) for all build settings will resolve to CONFIGURATION_Debug for builds under the ‘Debug’ build setting, but will resolve to CONFIGURATION_Release for builds under the ‘Release’ setting. [HockeyApp][http://www.hockeyapp.net] recommends using this same technique to keep their Beta Testing code out of your release builds for the app store.

With great power…

I’d be remiss if I didn’t state that this technique should be used extremely sparingly. The use cases presented here are examples of how you may want to use source code preprocessing, but you can quickly go too far. Used correctly and sensibly however, Preprocessors can be a powerful addition to your workflow. You just have to know where to find them.

dancroak

2011 Rubyist’s guide to a Mac OS X development environment

It’s been two and a half years since my last laptop. It’s neat to look back and see how much has improved since then for setting up a Ruby development environment.

Of particular note, Homebrew, RVM, and Bundler did not exist back then.

Here’s how I set up an OS X 10.7 (Lion) thoughtbot laptop in 2011.

GCC

I need GCC to help install everything else so I downloaded GCC for Lion.

We used to have to install XCode to get GCC when OS X wasn’t for developers, which was a 3-4GB download and took 10GB+ of space. Buzzkill.

However, Kenneth Reitz, one of the Readability guys, fixed this with his OS X GCC installer, which is a comparatively svelte 272MB download.

Later on, when we’re installing things using Homebrew, we’ll see warnings like:

Xcode is not installed! Builds may fail!

But, the builds will build fine.

While that’s installing, we’ll customize our environment a little.

SSH

I need a public key to get access to private Github repositories.

ssh-keygen -t rsa

I’m kept hitting “enter” until it was done. Alternatively, I could have brought my old SSH key over but I’m not into falconry.

dotfiles

We have a standard set of configurations for vim, irb, git, zsh, and more.

I cloned the repo:

git clone git://github.com/thoughtbot/dotfiles.git

I ran the installer:

./install.sh

This sets up the appropriate symlinks (~/.vimrc, ~/.irbrc, etc.). I’ll stay up-to-date and contribute using the fork-track-update flow described in the README.

zsh

Our dotfiles assume zsh so I switched from the bash default to zsh:

chsh -s /bin/zsh

Re-map Caps Lock to Control

We’re pretty much all vim users here so it’s nice having super-quick home-row access to the Control key… and who uses Caps Lock, anyway?

System Preferences > Keyboard > Modifier Keys

git

Already installed by default, but I set the global config:

git config --global user.name "Your Name"
git config --global user.email you@example.com

Heroku accounts

I’m using Heroku for all my apps right now. However, thoughtbot’s clients and even our own apps like Trajectory are not owned by my Heroku account. So, it comes in handy to be able to switch to a different account on a project basis.

heroku plugins:install git://github.com/ddollar/heroku-accounts.git
heroku accounts:add dan  --auto
heroku accounts:add thoughtbot  --auto
heroku accounts:add client  --auto
heroku accounts:default dan

thoughtbot’s laptop script

Once GCC is downloaded and installed, I’m ready for the heavy-duty installation using our laptop script.

bash < <(curl -s https://raw.github.com/thoughtbot/laptop/master/mac)

This installs:

  • Homebrew (for managing operating system libraries)
  • Postgres (for storing relational data)
  • Redis (for storing key-value data)
  • Ack (for finding things in files)
  • Tmux (for saving project state and switching between projects)
  • ImageMagick (for cropping and resizing images)
  • RVM (for managing versions of the Ruby programming language)
  • Ruby 1.9.2 stable (for writing general-purpose code)
  • Bundler gem (for managing Ruby libraries)
  • Rails gem (for writing web applications)
  • Heroku gem (for interacting with the Heroku API)
  • Taps gem (for pushing and pulling SQL databases between environments)
  • Postgres gem (for making Ruby talk to SQL databases)

It took about 15 minutes for everything to install.

While it’s running, it copies your SSH key to the clipboard and opens your Github SSH page. Paste your SSH key so your Github account is authenticated to your machine.

Caveats

We wrote a laptop script because we help hundreds of people a year get a Ruby development environment set up at workshops and Boston Ruby hackfests. One time we set up 30 business school students’ laptops in 3 hours.

If you read our source, you’ll see it it’s very simple but more invasive than, say, the excellent Cinderella by Corey Donohoe, which uses Chef to keep your machine tidy. We’re assuming the person definitely wants a “thoughtbot laptop”.

DIY

It’s pretty easy to write a wrapper that installs Homebrew, RVM, and your favorite databases and gems so consider forking our project and writing your own script, just like you might write your own Rails template script like Suspenders.