Thoughts on iOS build tools

Up until now you had 2 good ways to build and sign your application from the command line:

  • Use the built-in xcodebuild command
  • Use the third party tool shenzhen

Both approaches have their problems


When using xcodebuild you have to fiddle around with a long list of available parameters:

That's not something you remember and just enter every time you want to build an ipa file. The command above will generate a huge amount terminal output (talking > 10MB). For every source file that gets complied xcodebuild prints out around 60 lines of information you don't really care about:

The best way to fix the output annoyance is to use xcpretty in which you can pipe the xcodebuild ouput.


shenzhen was the easiest way to build your iOS application: It took care of generating the xcodebuild commands to build your application. But instead of showing a clean output while building, it omitted the complete output of the whole process. 

The main issue with shenzhen was the information you received when you run into an error: 

You know what you'd need to do to solve this issue? No, it's not clear, you don't get any information that helps you fix this issue. That's a simple code signing issue, in which the project defines a provisioning profiles that doesn't exist.

shenzhen is a great tool, but hasn't received any updates recently. With fastlane I used a fork of shenzhen to fix some bugs. After a time I decided to build 'gym', a tool which should replace the building part of shenzhen. (shenzhen also does distribution to beta testing services)

Improving the process

In my opinion it's really important a tool does different things depending on the outcome:

  • When the tool succeeded, the user doesn't need a lot of output/information, except that everything worked fine
  • When something went wrong while building, the user should get as much relevant information as possible to make it easy to quickly fix the issue.

Tools should actively help the user resolve common issues. By showing as much useful information as possible and even telling the user how to fix it, a tool automatically becomes much more useful.

Tools can automatically detect all kinds of issues and help the user fix them really fast:

When gym succeeds the terminal output is minimalistic but still shows relevant and useful information while processing

Another important point is being compatible with existing technologies. With gym for example I want the user to still be able to use the Xcode Organiser to distribute the archive or dSYM file


When writing about developer tools I don't want to forget the famous "Fix Issue" button. 

 How can we improve it? 

The most important things a developer tool should do

  • Expose information about what Xcode is going to try when you click "Fix Issue"
  • Show a log of what was tried and what failed
  • If possible show the user how to fix an issue manually

In the example of gym you get every shell command that is being executed printed out so you can manually try fixing something in case the build process fails.

If you need even more information, the you can use --verbose flag.


Besides standard features like sensible defaults and convention over configuration, I think it's important the user knows what's going on with the tool. This allows the users to debug issues themselves. By being transparent you build up trust. Developer see what the tool is doing.

You can check out gym on GitHub:

Letting computers do the hard work

As iOS developers we're still used to doing many manual tasks. It's an issue that exists because everything is still new. Just ask your back-end developer about the last time they manually deployed a new version directly to the production server. The answer should be: Oh, I was still in high school back then. Why? Because there are many mechanisms before the actual release to avoid broken releases that don't pass the tests.

Using fastlane you can already automate a large part of your daily development tasks, but one thing was missing:

Have you ever been to an airport, where you had to ask the manager of the airport if you can board now? Once the manager agrees, you'll be carried from your check-in to your gate into your plane.

Because that's what you are doing right now as an app developer when you want to invite a beta tester to your TestFlight app. And you even repeat that for every single beta tester you invite.

Right now, you have to go through 9 steps (on the right) just to invite one beta tester to your TestFlight program.

Have you ever asked a blog publisher if they add you to their newsletter? No, that's not how that works!

I just launched boarding, a tool that allows you to launch your own TestFlight Invite page in under 3 minutes.

More information about this project can be found on GitHub.

About Automation

If you're reading this post, chances are high you are an iOS/Mac developer. You get paid for developing iOS applications which is probably what you're best in.

Are you responsible for...?

  • Creating and uploading screenshots
  • Updating app metadata like the description
  • Manually building and uploading a beta build when your boss tells you to
  • Inviting new beta testers to your beta testing service

You probably said yes to some of those points. In my experience most developers do those things manually. Why?

We haven't got time to automate this stuff, because we're too busy dealing with the problems caused by our lack of automation. - @bitfield

While some developer may enjoy doing passive activities, you usually want to get your job done. That means working on the iOS/Mac app itself working on awesome new features.

That's why we have to follow our friends from the back-end team and start automating tedious processes. Instead of us, computer should do those tasks.

As you can see, the users interested in beta testing your application accesses your Heroku application directly to enter their email address. The Heroku app will then use spaceship to communicate with iTunes Connect directly without any interaction from a real person. Spaceship automatically registers the new testers, adds them to the application and sends out the TestFlight email. 

You as an developer are in no way involved in this process. Once you merge into your beta branch, fastlane will go ahead and build, sign and upload your app, resulting in automatic emails to all your testers. (this depends on how you use fastlane)

If you tried the Heroku button, you'll see there are no additional manual steps required to get a web service running. You enter your credentials, choose your application and the web service is up and running. 

What's next?

The primary use case of boarding was not to actually solve this certain problem, but to demonstrate what's possible using spaceship. In only 24 hours I had the idea for boarding, started working on it and released it publicly.

Think about what you can build with spaceship. It's a foundational tool to communicate with Apple's web services, both the Apple Developer Portal and iTunes Connect allowing you to build all kinds of cool things!

Some random ideas:

  • Automatically sync iTunes Connect users with your company internal user system
  • Automatic daily backups of all iTunes Connect and Apple Dev Portal data
  • Automatically send customers emails once a new version of their app got approved
  • Build a lightweight dashboard for your clients to see a grid of their apps with the current download numbers, reviews and app review status
  • Using the same change-log text for all languages? Why should you copy&paste the text manually to 10 languages?
  • Added App Review information to your company dashboard or push them directly to Slack

Managing fastlane feedback

I'm happy to say, most feedback I receive for the fastlane projects is very positive. I think that's mostly because it's something that's completely new in the iOS field and people just realised: Hey, we can actually automate those kind of things.

Sometimes we forget the happiest moments. Remember the time your role model tweeted about you or one of your projects? Remember the time a well known developer retweeted your last tweet? It's the best thing, you are insanely happy for a while, but forget about it again a few days later. 

But that doesn't only apply to tweets: How about the last time one of your projects became a trending repository on GitHub? What about that email you received from a fan who just wanted to say Thanks?

It might be just be me, but I don't want to lose that "moments". I even collected screenshots of the "follow" notifications I received when some of my favourite developers like @orta, @tapbot_paul ,@ashfurrow, @javi@soffes and @kylefuller started following me on Twitter.

I just didn't want to lose all this. That's why I collected the best tweets and screenshots since the initial launch of deliver

Open All Tweets


Besides tweets, you probably want to remember all kinds of things: comments on Reddit, the number of visitors you had on launch day and smaller things like the number of git commits you had on that one day.


I'm still using Sparrow, the best mail client for the Mac. To store an email, I just drag and drop the email into the fastlane feedback folder.

And now what?

So why do all this? I'm not a super organised person, but I try my best to do this right. After storing all this, I usually print it all and store it somewhere save. All this just because you never know what formats will be readable in 20 years and if you "lose" some files because you forgot to migrate some of those files. 

Check out the full list of tweets

Creating perfect App Store Screenshots of your iOS App

Your App Store screenshots are probably the most important thing when it comes to convincing potential users to download or purchase your app. Unfortunately, many apps don't do screenshots right.

A quick overview over existing methods to generate screenshots:

Manually create screenshots on all devices and all languages

It comes without saying that this takes too much time, which also decreases the quality of the screenshots. Since it is not automated, the screenshots will show slightly different content on the various devices and languages. Many companies end up creating screenshots only in one language and use it for all languages. While this might seem okay to us developers, there are many potential users out there, that cannot read the text on your app screenshots if they are not localised. Have you ever looked at a screenshots with content in a language you don't know? It won't convince you to download the app.

Biggest disadvantage of this method: If you notice a spelling mistake in the screenshots, you release an update with a new design or you just want to show more up to date content, you'll have to create new screenshots for all languages and devices... manually.

Please click on the pictures to enlarge them for more details.

Notice how the size of the font of the first 3 screenshots (iPhone 4, iPhone 5, iPhone 6) are exactly the same. Only the last screenshot (iPhone 6 Plus) the text seems larger as it's a @3x display.

Create screenshots on one device type, put it into frames and resize them

This way, you only create 5 screenshots per language on only one device type and put them into frames. By putting the one screenshot into different frames, the tool you use can resize the resulting image to match the iTunes Connect requirements. 

Below some example applications that use this technique. I only had to upload one screenshot and got the result shown below. (left: iPhone 4, right: iPhone 6 Plus)

Do you see the difference between the font sizes in the screenshots? Carrier is easily readable on the iPhone 6 Plus and maybe the iPhone 6, but not on the other devices.
Another problem with this service is the wrong device types: The iPhone 6 should not look the same as the other devices.

A different example which now uses the correct device frame for each screen size. Do you see how very small the font on the iPhone 4 is? All 4 frames use the exact same screenshot. On smaller devices this results in very small fonts which are difficult to read for the end-user. On larger devices the screenshot is scaled up, which causes blurry images and fonts.

Don't get me wrong, using a web service that does these kind of frames for you is a great and easy way to get beautiful screenshots for the App Store. It's also the best solution if you don't want to invest more time automating taking better screenshots.

Update August 2016: Use just one set of screenshots

With the iTunes Connect screenshot update on August 8th, 2016, you can now use one set of screenshots for all available devices and screenshots. iTunes Connect will automatically scale the images for you, so that each device renders the same exact image. 

While this is convenient, this approach has the same problems as the device frame approach: The screenshots don't actually show how the app looks on the user's device. It's a valid way to start though, since you can gradually overwrite screenshots for specific languages and devices. 

To sum up, the problems with existing techniques:

  • Wrongly scaled screenshots resulting in blurry font
  • Not using the correct device frames for the various screen sizes
  • Screenshot doesn't show the screen the user will actually see (iPhone 6 Plus user interface should look differently than iPhone 4)
  • Most screenshot builder don't have landscape support
  • No Mac App Support

Using correct screenshots for all device types and languages ("The Right Way")

Checklist for really great screenshots:

  • Screenshots localised in all languages your app supports
  • Different screenshots for different device types to have the correct font in your screenshots
  • Same content in all languages and device types (means same screens visible with the same items)
  • No loading indicators should be visible, not even in the status bar
  • No scrolling indicators should be visible
  • A clean status bar: Full battery, full Wifi and of course 9:41
  • Localised titles above your screenshots
  • Device in screenshots actually matches the device of the user (except for the color)
  • A nice looking background behind the frames
  • Optionally a coloured title around the device frame

Clean Statusbar

Notice the following things:

  • 9:41 AM (or just 9:41)
  • All 5 dots (formerly known as bars)
  • Full WiFi Signal
  • Full battery

To achieve such a nice looking status bar, I can really recommend SimulatorStatusMagic by Dave Verwer. It's very easy to setup and you get all the above mentioned points for free.

You can also use the Quick Time recording feature, by connecting your real device via USB. This works nicely, however it requires you to own a physical device for each screen size.

Nothing is perfect

I worked on screenshot automation for a really long time, but haven't found the ultimate solution (yet). Even with tools like snapshot and frameit there are some open issues. 

Okay, back to what you can actually do today :) Below are the results of nice screenshots, which were all generated completely automatically.

What's wrong with those screenshots? The time isn't 9:41.

On the iPhone 4, iPhone 5 and iPhone 6 the font size is exactly the same. The iPhone 6 Plus, again, has a @3x display which is why the text appears larger.

How does this look like for landscape screenshots?

Since the above screenshot collection looks a bit messy I decided to automatically resize the screenshots in the following examples. Instead of leaving all screenshots 1:1 they now appear properly aligned next to each other.

Landscape screenshots of MindNode: The iPhone 6 Plus shows a split screen when the app is in landscape mode. The smaller screen sizes show the list only. The users can see how the app looks like on their device before even installing the app.
Another interesting detail: Take a look at the lock button on the different devices, on the 2 screenshots on the top the lock button is on the top of the iPhone, while the lock button is on the right side on the latest generation.
The screenshots don't have a status bar, since MindNode doesn't show it in landscape mode.

Special thanks to Harald Eckmüller for designing the MindNode screenshots.

How does this look like for multiple screenshots?

How does this look like when you support multiple languages? 

Generating this amount of screenshots takes hours, even when it is completely automated. The nice thing: You can do something else on your Mac while the screenshots are generated, as long as you don't need the simulator. Instead of working while the screenshots are generated, you can also take a nap or tweet about fastlane.

How does this magic work?

All MindNode screenshots shown above are created completely automatically using 2 steps:

Creating the Screenshots

Using snapshot you can take localized screenshots on all device types completely automatic. All you have to do is provide a JavaScript UI Automation file to tell snapshot how to navigate in your app and where to take the screenshots. More information can be found on the project page. This project will soon be updated to use UI Tests instead of UI Automation to write screenshot code in Swift or Objective C instead.

This step will create the raw screenshots for all devices in all languages. At this point you could already upload the screenshots to iTunes Connect, but this wouldn't be so much fun.

Adding the device frame, background and title

frameit was originally designed to just add device frames around the screenshots. With frameit 2.0 you can now add device frames, a custom background and a title to your screenshots.

  • Custom backgrounds
  • Use a keyword + title to make the screen look more colorful
  • Use your own fonts
  • Customise the text colors
  • Support for both portrait and landscape screenshots
  • Support for iPhone, iPad and Mac screenshots
  • Use .strings files to provide translated titles

The same screenshot on the iPhone, iPad and Mac.

Take a closer look at the screenshots above: The iPad's time is 9:41 and the carrier name is MindNode. The other screenshots don't have a status bar, as MindNode doesn't show it on the iPhone in landscape mode.

A timelapse video of snapshot creating the MindNode screenshots.

The generated HTML Summary to quickly get an overview of your app in all languages on all device types.

How can I get started?

To make things easier for you, I prepared an open source setup showing you how to use frameit to generate these nice screenshots, available on GitHub.

The interesting parts are:

All you have to do now is run frameit white, to frame all the screenshots generated by snapshot.

Putting things together

Calling snapshot and frameit after each after is far too much work, let's automate this.

Take a look at the fastlane configuration of MindNode: Fastfile

To generate new screenshots, frame them and upload them to iTunes Connect you only have to run 

    $ fastlane ios screenshots

More Information

snapshot, frameit and deliver are part of fastlane.

Special thanks to MindNode for sponsoring frameit 2.0 and providing the screenshots for this blog post.