In this fastlane tutorial you’ll learn how to take advantage of many of the tools provided to automate large and laborious parts of your life as an iOS dev!

Note: This tutorial has been updated for Xcode 8.2, Swift 3 and Fastlane 2.6.0 by Lyndsey Scott. The original tutorial was written by Satraj Bambra.

It’s that wonderful moment: You’ve poured days, weeks, or maybe even months into building an amazing app, and it’s finally ready to share with the world. All you have to do is submit it to the App Store. How hard could that be, right?

Cue mountains of grunt work: Capturing tons of screenshots, fighting Xcode provisioning, uploading to the App Store and constant mindless work! Aargh!

Wouldn’t it be nice if there was a better way? If only you could run a single command that took all your screenshots, on all your supported devices, in every supported language automagically. If only there were a single command to upload those screenshots, generate your provisioning profiles and submit your app. Think of all the time you’d save!

Well, you’re in luck. :] Thanks to the amazing Felix Krause, there’s now a tool to do all this and more! It’s called fastlane, and it’ll become your new best friend.

In this fastlane tutorial, you’ll learn how to use fastlane to deploy an app to the App Store. You’re in for a fast ride, so buckle up and hang on tight!

Note: This tutorial uses the command line extensively. While you don’t need to be a Terminal expert, you’ll need to have basic knowledge for how the command line works. This tutorial also assumes you know about code signing and iTunes Connect. If you’re unfamiliar with these, please read this tutorial series first.

Getting Started

Download the starter project here, and save it to a convenient location.

mZone, the sample app you’ll use in this tutorial, is a simple poker calculator for No Limit Texas Hold’em tournaments. It displays a recommended action based on your chip count and the current big blind level:

Open the project in Xcode to see the app yourself, then navigate to the mZone Poker target’s Build Settings. In the Product Bundle Identifier field, you’ll find com.mZone.mZone-Poker-xxx:

Replace “xxx” with your email address sans “@” and “.” so that the bundle identifier for your project is different from every other app identifier on iTunes Connect.

To get fastlane up and running the following are required:

OS X 10.9 (Mavericks) or newer

Ruby 2.0 or newer

Xcode Command Line Tools (CLT)

Paid Apple Developer Account

Since fastlane is a collection of Ruby scripts, you must have the correct version of Ruby installed. Fortunately, OS X 10.9 (Mavericks) and later come with Ruby 2.0 by default. You can confirm this by opening Terminal and entering the following command:

ruby -v

To check whether the Xcode CLT are installed, enter the following into Terminal:

xcode-select --install

If Xcode CLT are already installed, you will get this error: command line tools are already installed, use "Software Update" to install updates . If not, it will install Xcode CLT for you.

With the prerequisites completed, you’re ready to install fastlane. Enter the following command:

sudo gem install -n /usr/local/bin fastlane --verbose

/usr/local/bin is still writeable which is why you’re installing fastlane there. Note: With El Capitan, OS X introduced System Integrity Protection , also known as “Rootless”, which prevents users from having root access by limiting system access.is still writeable which is why you’re installing fastlane there.

After entering your system password, you will see a bunch of activity in your Terminal window, indicating the installation is in progress. This could take a few minutes, so grab some coffee, walk your dog or brush up on your zombie-fighting tactics. :]

When the installation completes, you’ll be ready to set up your project to use fastlane. But before you do, let’s take a high-level look at the fastlane tools.

The fastlane Toolchain

To work its magic, fastlane brings the following set of tools all under one roof:

produce creates new iOS apps in both iTunes Connect and the Apple Developer Portal.

cert automatically creates and maintains iOS code signing certificates.

sigh creates, renews, downloads and repairs provisioning profiles.

snapshot automates taking localized screenshots of your iOS app on every device.

frameit puts your screenshots into the right device frames.

gym builds and packages your iOS apps.

deliver uploads screenshots, metadata and your apps to the App Store.

pem automatically generates and renews your push notification profiles.

spaceship is a Ruby library able to access the Apple Developer Center and iTunes Connect APIs.

pilot automates TestFlight deployments and manages beta testers.

boarding invites beta testers.

match syncs certificates and provisioning profiles across your team using Git.

scan runs tests on your apps.

You will be using many of these tools in the deployment process of the sample app.

That’s enough theory– it’s time to put fastlane in the fast lane!

Setting up fastlane

First, open Terminal and cd into your mZone project location. For example, if you’ve added the mZone folder to your desktop, you can enter:

cd ~/Desktop/mZone

to set mZone as the working directory.

Once you are in the mZone folder, enter the following command:

fastlane init

Note: If you get a “permission denied” error, you will need to prefix this command with sudo .

Next, enter your Apple ID to kickstart the process.

fastlane uses deliver to sign you into both iTunes Connect and the Apple Developer Portal, and it also verifies an app exists in your account with a matching app identifier. Since this is a new app, it won’t exist, and it will need to be created.

Connection reset by peer - SSL_Connect Note: if you get this error try updating your ruby version as fastlane suggests to do. Unless you’re already using rbenv or rvm (which are Ruby version managers), the easiest way to do this is via Homebrew. First, install Homebrew by entering this Terminal command: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" Next, install Ruby using the `brew` command: brew update && brew install ruby Homebrew might also tell you that you need to run brew link --overwrite ruby . You may also need to open a new Terminal session. Then, install fastlane again without specifying an install path: sudo gem install fastlane --verbose Lastly, run fastlane init again, and it should be able to create the app on iTunes Connect.

You should see the following output:

This app identifier doesn't exist on iTunes Connect yet, it will be created for you This app identifier doesn't exist on the Apple Developer Portal yet, it will be created for you Please confirm the above values (y/n)

Enter “y” to confirm the detected values.

Since I’ve already created an app called “mZone Poker” on iTunes Connect, you’ll next be prompted:

It looks like that mZone Poker has already been taken by someone else, please enter an alternative App Name:

Type in a unique app name. (Similar to my recommendation for the bundle ID, I recommend “mZone Poker” followed by your email address sans “@” and “.” to ensure the app name is unique.)

After some time (as little as 30 seconds and as much as 30 minutes depending on the emotional state of Apple’s occasionally volatile servers), you’ll be informed that your app was created on iTunes Connect.

Open the mZone project folder. You will notice that your project now has a fastlane folder:

The relevant files are:

Appfile, which stores the app identifier and your Apple ID.

Fastfile, which manages the lanes you create to call certain actions.

Deliverfile, which lets you add the required metadata when submitting your app to the App Store.

Within the mZone Project folder, navigate to fastlane\metadata. You will notice a bunch of text files there that contain common App Store items like the description, keywords, categories, etc. These files are used to set your app’s metadata information displayed on the App Store.

Open en-US/description.txt and add the following text:

mZone is a simple poker calculator for No Limit Texas Hold’em tournaments that displays a recommended course of action based on your chip count and the current big blind level.

Add the following to keywords.txt:

Poker, Cards, Gambling

Check that name.txt already contains the name of your app, then type http://www.raywenderlich.com into both privacy_url.txt and support_url.txt.

While this app supports both French and English, only the en-US folder exists.

To fix this, simply make a copy of the folder and call it fr-FR. In the interests of keeping this fastlane tutorial shorter, we won’t actually provide real French translations now. Your project folder should now look like this:

Next, in the metadata folder:

Add Copyright (c) 2016 Razeware LLC to copyright.txt

to copyright.txt Add Games to primary_category.txt

to primary_category.txt Add Card to primary_first_sub_category.txt

to primary_first_sub_category.txt Add Casino to primary_second_sub_category.txt

Then, in the same folder, use your favorite text/code editor to create a json file named itunes_rating_config.json containing the following:

{ "CARTOON_FANTASY_VIOLENCE": 0, "REALISTIC_VIOLENCE": 0, "PROLONGED_GRAPHIC_SADISTIC_REALISTIC_VIOLENCE": 0, "PROFANITY_CRUDE_HUMOR": 0, "MATURE_SUGGESTIVE": 0, "HORROR": 0, "MEDICAL_TREATMENT_INFO": 0, "ALCOHOL_TOBACCO_DRUGS": 0, "GAMBLING": 2, "SEXUAL_CONTENT_NUDITY": 0, "GRAPHIC_SEXUAL_CONTENT_NUDITY": 0, "UNRESTRICTED_WEB_ACCESS": 0, "GAMBLING_CONTESTS": 0 }

This iTunes rating configuration lets iTunes Connect know that out of all the ratings criteria, the app only contains “frequent/intense” simulated gambling (i.e. value = 2). This file gives iTunes Connect the information it requires to give the app the age rating Apple has deemed appropriate.

And lastly, download the App Store icon here and also add it to the metadata directory.

Congratulations! You’ve added all the metadata required for submission. It’s time to to start using fastlane. :]

Note: You can find a full list of keys for these fastlane settings here

Creating Certificates and Provisioning Profiles

Open Fastfile in a text editor of your choice, disable smart quotes if your text editor supports them, then replace the contents of the file with the following code:

# This is the minimum version number required. # Update this, if you use features of a newer version fastlane_version "2.6.0" default_platform :ios platform :ios do # 1 desc "Creating a code signing certificate and provisioning profile" # 2 lane :provision do # 3 produce( app_name: 'YOUR_UNIQUE_APP_NAME', language: 'English', app_version: '1.0', sku: '123abc' ) # 4 cert # 5 sigh(force: true) end error do |lane, exception| # This block is called if there was an error running a lane. end end

Replace YOUR_UNIQUE_APP_NAME with the app name you specified earlier. Your iTC username and the app identifier are loaded automatically from the Appfile, so you don’t need to provide them here.

Also replace the fastlane version with the most recent version if it’s no longer 2.6.0 (and it will most probably not be 2.6.0 since fastlane is well-maintained and updated frequently).

Note: To find out your current version, enter fastlane -v into the terminal. If at any point, fastlane tells you to update to a newer version, but running sudo gem update fastlane outputs that there is “Nothing to update,” perhaps the ruby manager you’re using isn’t up to date. Run gem sources --add https://rubygems.org/ to install Ruby Gems since it’s likely to produce the most current information.

If you’ve never seen Ruby before, this may look like gibberish to you, so here’s what this code does:

Provides a description for the lane. A lane is a workflow of sequential tasks. Names this lane provision. Uses produce to create a base app in both iTunes Connect and the Developer Portal with the specified identifier, name, language and version number. Uses cert to create a new private key and signing request, download and install the generated certificate and import all generated files in your keychain. Uses sigh to generate a provisioning profile. By specifying force: true , a new provisioning profile is created on each run; this ensures you’re always using the correct code signing certificate.

Note: sigh creates an App Store distribution profile by default. If you want to create an ad hoc profile you would need to specify sigh(adhoc: true) . For a development profile it would be sigh(development: true) . For simplicity, you’re only making a distribution profile in this tutorial.

Woohoo! You have now created your very first lane. Save the file, open Terminal inside your project folder and enter the following command:

fastlane provision

This tells fastlane to run your provision lane.

After a minute or so, fastlane asks for your iTunes Connect password, which is safely stored in your computer’s keychain. Enter your password, and upon successful completion, your Terminal window should output the “fastlane summary”:

Note: If you see any errors, particularly one about “Creation of apps of this type is not available,” log in to iTunes Connect and make sure you don’t have any updated agreements waiting to be signed.

Log in to iTunes Connect, and voila! Your app has already been created. How cool is that?

In order to use the provisioning profile you just created, you’ll have to make modifications in Xcode. Open the mZone Poker.xcodeproj, navigate to mZone Poker Target\Build Settings\Code Signing and set your Provisioning Profile to the newly created <app ID> AppStore. Then choose your Code Signing Identity based on this provisioning profile as shown below:

Note that your code signing identity will be based on the identities available in the provisioning profile. By doing this, your app can use the newly created provisioning profile when gym builds the IPA file.

Navigate to General, deselect Automatically manage signing then set both the Signing (Debug) and Signing (Release) to that same provisioning profile you just specified.

With just a few commands and settings, you have added an app to your Dev Portal and iTunes Connect, created a provisioning profile and code signed your app. You have already saved yourself hours of work! :]

Screenshots Made Easy

When submitting an app, taking screenshots is by far the most tedious task. The more useful your app is (meaning the more devices and languages it supports), the more hours of valuable time you’ll burn taking screenshots. Painful!

mZone supports two languages, English and French, and four screen sizes. If you had to take five screenshots per device for each language and screen size, that would be 40 screenshots! With fastlane, however, you can do all this by running a single command.

But first, set up the project for snapshot by entering the following in Terminal:

fastlane snapshot init

A Snapfile file will now appear in your fastlane folder. Snapfile lets you specify the devices and languages you want to provide screenshots for.

Open Snapfile and replace the contents of the file with the following code:

# A list of devices you want to take the screenshots from devices([ "iPhone 5", "iPhone 6", "iPhone 6 Plus" ]) # A list of supported languages languages([ 'en-US', 'fr-FR' ]) # Where should the resulting screenshots be stored? output_directory "./fastlane/screenshots" # Clears previous screenshots clear_previous_screenshots true # Latest version of iOS ios_version '10.1'

This simply sets the devices you want to support, the languages the app supports and specifies the location of the current screenshots directory. clear_previous_screenshots will clear any previously taken screenshots.

Wait a second… Why isn’t the iPhone 7 and iPhone 7 Plus on that device list? That’s because the screenshots on iTunes Connect are categorized by display size, not device; and since the iPhone 6 & 7 both have 4.7 inch displays, and the iPhone 6 Plus & 7 Plus both have 5.5 inch displays, including both the 6’s and 7’s in our screenshots folder would produce duplicate screenshots in iTunes Connect.

Save the file and close it.

Return to your terminal and note the instructions that appeared after running fastlane snapshot init :

Open your Xcode project and make sure to do the following: 1) Add a new UI Test target to your project 2) Add the ./fastlane/SnapshotHelper.swift to your UI Test target You can move the file anywhere you want 3) Call `setupSnapshot(app)` when launching your app let app = XCUIApplication() setupSnapshot(app) app.launch() 4) Add `snapshot("0Launch")` to wherever you want to create the screenshots

So that’s what you’ll do next.

Again open mZone Poker.xcodeproj in Xcode, then navigate to File\New\Target, within the iOS tab’s Test section, select iOS UI Testing Bundle then hit Next\Finish.

Return to your mZone folder’s fastlane directory then drag SnapshotHelper.swift into the newly created mZone PokerUITests folder in Xcode.

Open mZone_PokerUITests.swift, remove both the setUp and tearDown methods, then and add the following code within testExample :

// 1 let app = XCUIApplication() setupSnapshot(app) app.launch() // 2 let chipCountTextField = app.textFields["chip count"] chipCountTextField.tap() chipCountTextField.typeText("10") // 3 let bigBlindTextField = app.textFields["big blind"] bigBlindTextField.tap() bigBlindTextField.typeText("100") // 4 snapshot("01UserEntries") // 5 app.buttons["what should i do"].tap() snapshot("02Suggestion")

This code will help create your screenshots at certain points within the app’s execution. Here’s what you’re doing bit by bit:

Set up your test to take snapshots and launch the app. The launched app will automate tapping the Chip Count text field (the accessibility identifier of which was pre-set to “chip count” in the Storyboard); and then it will automate entering the number 10 into that field. Then the app will tap the Big Blind text field (pre-set with the accessibility identifier of “big blind”) and enter the number 100 into that field. Take a snapshot at this point in order to produce a screenshot showing how the app looks with user entries. Automate tapping the What Should I Do? button, then take another screenshot to show the resulting alert.

Close Xcode, open Fastfile and add the following code right above error do |lane, exception| :

desc "Take screenshots" lane :screenshot do snapshot end

Here you’re creating a new lane called screenshot that uses snapshot to take screenshots as specified by the Snapfile, which you just edited.

Save the file, return to Terminal and enter:

fastlane screenshot

Now watch … the screenshots are captured without you having to do anything else! Bask in the joy of skipping grunt work. :]

After the process finishes, the html file screenshots.html should automatically open upon screenshot completion and you can scroll through all the screenshots fastlane has taken.

Note: In order for snapshot to take screenshots, it needs access to the simulator equivalents of the devices listed in Snapfile. If you are missing one or more devices from that list, add them in Xcode by going to Window\Devices. In the bottom left corner, you can use the + to add new simulators.

You now have all your device screenshots in both English and French in just one Terminal command – it doesn’t get better than that!

Note: If you see warnings about ambiguous simulator names, you may need to delete some of your simulators or change the contents of your Snapfile. snapshot can work in tandem with Xcode’s UI tests to give you screenshots of specific parts of the app as well! All the more reason to use tests. :]

Creating the IPA file

Sure, it’s nice that you don’t have to create screenshots anymore, but the most time-consuming part of the submission process is actually building and signing the app. Guess what – fastlane can do this as well!

Open Fastfile and add the following code after the end of the screenshot lane:

desc "Create ipa" lane :build do increment_build_number gym end

This creates a lane called build which uses increment_build_number to increase the build number by 1 (so each build number is unique per iTunes Connect’s upload requirement) and gym to create a signed ipa file.

Save Fastfile, then inside the mZone project directory in Terminal, enter the following command:

fastlane build

This calls the build lane you added above that starts the build process. Once it successfully completes, open the mZone Project folder. You should see the signed ipa file:

Done! This one command takes care of the arguably most frustrating and least-understood part of iOS development.

Seamless Delivery

To upload the screenshots, metadata and the IPA file to iTunes Connect, you can use deliver, which is already installed and initialized as part of fastlane.

Open Fastfile and add the following code after the end of the build lane:

desc "Upload to App Store" lane :upload do deliver end

Open your Terminal window and enter the following command:

fastlane upload

With this command, fastlane creates a preview of what it will upload in the form of an HTML page.

If everything looks good, type y into Terminal in answer to the question “Does the Preview on path ‘./Preview.html’ look okay for you? (y/n)”.

At this point you can just chill out and let the computer do all the work for you. :]

After the successful completion of the process, your Terminal window should look like this:

Log in to your iTunes Connect account. All screenshots, description and build version 1.0 should be uploaded and ready.

All that’s left for you to do is click the big “Submit for Review” button, and you’re done!

Wait just a minute… What’s all this about having to manually log in and click a button? I thought we were automating all the things.

Well, it turns out deliver can automatically submit your app for review as well!

First you need to update the upload lane:

desc "Upload to App Store and submit for review" lane :upload do deliver( submit_for_review: true ) end

Then you need to replace Deliverfile‘s contents with the following so it contains all the additional info needed for submission:

# 1 price_tier 0 # 2 app_review_information( first_name: "YOUR_FIRST_NAME", last_name: "YOUR_LAST_NAME", phone_number: "YOUR_PHONE_NUMBER", email_address: "YOUR_EMAIL_ADDRESS", demo_user: "N/A", demo_password: "N/A", notes: "No demo account needed" ) # 3 submission_information({ export_compliance_encryption_updated: false, export_compliance_uses_encryption: false, content_rights_contains_third_party_content: false, add_id_info_uses_idfa: false }) # 4 automatic_release false # 5 app_icon './fastlane/metadata/AppIcon.png' # 6 app_rating_config_path "./fastlane/metadata/itunes_rating_config.json"

Here you do the following:

Set your price tier to 0, indicating it’s a free app. Once you fill out the first four lines within app_review_information with the appropriate values, app_review_information provides your contact information to the app review team. Since fastlane doesn’t currently have a boolean-responsive key to indicate that no demo account is needed, you can simply indicate that no demo account is needed within the review notes. The submission information answers the questions you’re typically presented with after selecting the “Submit For Review” button. (You can find other available keys here.) Setting automatic_release to false allows you to release this version of the app manually after it’s accepted by app review. Provide the app icon file location to fastlane. Provide the iTunes Rating Configuration file location to fastlane.

Return to your terminal and again run:

fastlane upload

After several minutes, fastlane should indicate that you’ve successfully submitted your app for review and iTunes Connect should confirm it!

Note: As of fastlane version 1.111.0, the text output may incorrectly indicate your app has been submitted successfully even if there’s a problem during submission. So, you should always verify your app shows as Waiting for Review in iTunes Connect. Also note that in order to delete mZone from iTunes, your app must have been approved by an app review. After it’s been approved, you can briefly release and then select Remove from Sale in the pricing and availability section. Then, navigate to the App Information screen, scroll to the bottom and select Delete to remove the app permanently.

Putting it All Together

You currently have separate lanes for provisioning, screenshots, building and uploading to the App Store. While you could always call each of these one by one, you don’t want to do that, right?

Oh no, you want ONE command that does everything.

Open Fastfile and add the following code after the end of the upload lane:

desc "Provision, take screenshots, build and upload to App Store" lane :do_everything do provision screenshot build upload end

As this lane’s description and name implies, this lane takes care of everything. :]

Try it out by running this Terminal command:

fastlane do_everything

Great work! Just let fastlane do all the heavy lifting while you sit back and relax.

Caution: it might feel weird having so much time on your hands compared to your pre-fastlane existence, but trust us, you’ll get used to it. :]

Where to Go From Here?

Download the final project for this tutorial here (sans code signing, your app name, provisioning profile, certificate, ipa file, etc) to see how the app, metadata and fastlane files stack up to yours.

Today you learned how to use fastlane to deploy your apps and save you a bunch of time. Keep in mind that although there are a few steps necessary to get fastlane working, a lot of that is just first time setup.

fastlane also offers a ton of integrations that let you customize your lanes to provide real time feedback on Slack, perform unit tests and deploy TestFlight builds.

To learn more about this fantastic tool, take a look at the official fastlane website.

I hope you enjoyed this fastlane tutorial, and I look forward to seeing how you use fastlane in your deployment pipeline. If you have any questions or comments, please join the forum discussion below!