2019-03-06-ios-publishing-with-gitlab-and-fastlane.html.md.erb 12.1 KB
Newer Older
1 2 3 4 5 6 7
title: "How to publish iOS apps to the App Store with GitLab and fastlane"
author: Jason Lenny
author_twitter: j4lenn
categories: engineering
image_title: '/images/blogimages/ios-publishing-cover.jpg'
description: "See how GitLab, together with fastlane, can build, sign, and publish apps for iOS to the App Store."
William Chia's avatar
William Chia committed
tags: CI/CD, integrations, features
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
postType: product
ee_cta: false
twitter_text: "How to publish #ios apps to the @appstore with @gitlab and @FastlaneTools"

Recently we published a [blog post
detailing how to get up and running quickly with your Android app](/2019/01/28/android-publishing-with-gitlab-and-fastlane/), GitLab, and
[_fastlane_](https://fastlane.tools). In this edition, let's look at how to get
a build of an iOS app up and running, including publishing all the way to
TestFlight. To see how cool this can be, check out this [video
of me making a change on an iPad Pro using the GitLab Web IDE](https://www.youtube.com/watch?v=325FyJt7ZG8), getting that
built, and then receiving an update to the test version of my application on the
very same iPad Pro I was using to develop.

For the purposes of this article, we'll be using a [simple Swift iOS app](https://gitlab.com/jlenny/flappyokr)
that I recorded the video with.

## First, a note on Apple Store configuration

What we're going to need in order to set all of this up is an application set up
in the App Store, distribution certificates, and a provisioning profile that ties
it all together.

Most of the complexity here actually has to do with setting up your signing
authority for the App Store. Hopefully in most cases this is already good to go
for you; if you're a new app developer, I'll try to get you started on the right
track, but the intricacies of Apple certificate management is out of the scope of
this article, and tends to change somewhat frequently. But, this information
should get you going.

### My apps

Your application will need to be set up in App Store Connect so you have an ID
for your application, which will be used in your `.xcodebuild` configuration.
Your app profile and ID are what tie together the code builds with pricing and
availability, as well as TestFlight configuration for distributing testing
applications to your users. Note that you don't need to set up public testing –
you can use personal testing with TestFlight just fine as long as your testing
group is small, and the setup is simpler and requires no additional approvals
from Apple.

### Provisioning profile

In addition to the app setup, you need iOS distribution and development keys
created in the Certificates, Identifiers, and Profiles section of the Apple
Developer console. Once these certificates are created, you can create a
provisioning profile to unify everything.

Also note that the user you will authenticate with needs to be able to create
certificates, so please ensure that they have that ability or you will see an
error during the [_cert_ and _sigh_](https://docs.fastlane.tools/codesigning/getting-started/#using-cert-and-sigh)

### Other options

There are several more ways to set up your certificates and profiles than the
simple method I've described above, so if you're doing something different you may
need to adapt. The most important thing is that you need your `.xcodebuild`
configuration to point to the appropriate files, and your keychain needs to be
available on the build machine for the user that the runner is running as. We're
using _fastlane_ for signing, so if you run into trouble here or want to learn
more about your options, take a look at their extensive [codesigning documentation](https://docs.fastlane.tools/codesigning/getting-started/).

For this sample project, I'm using the [_cert_ and _sigh_](https://docs.fastlane.tools/codesigning/getting-started/#using-cert-and-sigh)
approach, but the [match
approach](https://docs.fastlane.tools/codesigning/getting-started/#using-match) may be better for actual enterprise use.

## Setting up GitLab and _fastlane_

William Chia's avatar
William Chia committed
### Setting up your CI/CD runner
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

With the above information gathered or set up, we can start with configuring the
GitLab runner on a MacOS device. Unfortunately, building on MacOS is the only
realistic way to build iOS apps. This is potentially changing in the future;
keep an eye on projects like [xcbuild](https://github.com/facebook/xcbuild) and
[isign](https://github.com/saucelabs/isign), as well as our own internal issue
[gitlab-ce#57576](https://gitlab.com/gitlab-org/gitlab-ce/issues/57576) for
developments in this area.

In the meantime, setting up the runner is fairly straightforward. You can follow
our most current [instructions for setting up GitLab Runner on macOS](https://docs.gitlab.com/runner/install/osx.html)
to get that up and running.

Note: Be sure to set your runner to use the `shell` executor. For building iOS on
macOS, it's a requirement to operate directly as the user on the machine rather
than using containers. Note that when you're using the shell executor, the
build and tests run as the identity of the runner logged in user, directly on
the build host. This is less secure than using container executors, so please
take a look at our [security implications documentation](https://docs.gitlab.com/runner/security/#usage-of-shell-executor)
for additional detail on what to keep in mind in this scenario.

sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
sudo chmod +x /usr/local/bin/gitlab-runner
cd ~
gitlab-runner install
gitlab-runner start

What you need to be careful about here is ensuring your Apple keychain is set up
on this host and has access to the keys that Xcode needs in order
to build. The easiest way to test this is to log in as the user that will be
running the build and try to build manually. You may receive system prompts for
William Chia's avatar
William Chia committed
keychain access which you need to "always allow" for CI/CD to work. You will probably
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
also want to log in and watch your first pipeline or two to make sure that
no prompts come up for additional keychain access. Unfortunately Apple does not
make this super easy to use in unattended mode, but once you have it working it
tends to stay that way.

### _fastlane_ init

In order to start using _fastane_ with your project, you'll need to run
`fastlane init`. Simply follow the [instructions
to install and run _fastlane_](https://docs.fastlane.tools/getting-started/ios/setup/), being sure to use the instructions in the
[Use a Gemfile](https://docs.fastlane.tools/getting-started/ios/setup/#use-a-gemfile)
section, since we do want this to run quickly and predictably via unattended CI.

From your project directory, you can run the following commands:

xcode-select --install
sudo gem install fastlane -NV
# Alternatively using Homebrew
# brew cask install fastlane
fastlane init

_fastlane_ will ask you for some basic configuration and then create a folder
called `fastlane` in your project which will contain three files:

#### 1. `fastlane/Appfile`

This file is straightforward, so you just want to check to make sure that the Apple
ID and app ID that you set up earlier are correct.

app_identifier("com.vontrance.flappybird") # The bundle identifier of your app
apple_id("your-email@your-domain.com") # Your Apple email address

#### 2. `fastlane/Fastfile`

The `Fastfile` defines the build steps. Since we're using a lot of the built-in
capability of _fastlane_ this is really straightforward. We create a single
lane which gets certificates, builds, and uploads the new build to TestFlight.
Of course, you may want to split these out into different jobs depending on your
use case. Each of these steps, `get_certificates`, `get_provisioning_profile`,
`gym`, and `upload_to_testflight` are pre-bundled actions already included with

`get_certificates` and `get_provisioning_profile` are actions associated with
the [_cert_ and _sigh_](https://docs.fastlane.tools/codesigning/getting-started/#using-cert-and-sigh)
approach to codesigning; if you're using [match](https://docs.fastlane.tools/codesigning/getting-started/#using-match)
or some other approach you may need to update these.


platform :ios do
  desc "Build the application"
  lane :flappybuild do

#### 3. `fastlane/Gymfile`

This file is optional, but I created it manually in order to override the default
output directory and place the output in the current folder. This makes things a
bit easier for CI. You can read more about `gym` and its options in the
[gym documentation](https://docs.fastlane.tools/actions/gym/).


### Our `.gitlab-ci.yml`

William Chia's avatar
William Chia committed
Now, we have a CI/CD runner associated with our project so we're ready to try a
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
pipeline. Let's see what's in our `.gitlab-ci.yml`:

  - build

  LC_ALL: "en_US.UTF-8"
  LANG: "en_US.UTF-8"

  stage: build
    - bundle install
    - bundle exec fastlane flappybuild
    - ./FlappyBird.ipa

Yes, that's really it! [We set UTF-8 locale for _fastlane_ per their
use a `clone` strategy with the `shell` executor to ensure we have a clean
workspace each build, and then simply call our `flappybuild` _fastlane_ target,
which we discussed above. This will build, sign, and deploy the latest build to

We also gather the artifact and save it with the build – note that the `.ipa`
format output is a signed ARM executable, so not something you can run in the
simulator. If you wanted a simulator output to be saved with the build, you
would simply add a build target that produces it and then add it to the artifact

### Other environment variables

There are some special environment variables behind the scenes here that are
making this work.


In order to authenticate against the App Store for the TestFlight upload,
_fastlane_ must be able to authenticate. In order to do this, you need to
create an app-specific password to be used by CI. You can read more about this
process in [this documentation](https://docs.fastlane.tools/best-practices/continuous-integration/#use-of-application-specific-passwords-and-spaceauth).

If you're using two-factor authentication, you'll also need to generate the
`FASTLANE_SESSION` variable – instructions are in the same place.


In order for [_cert_ and _sigh_](https://docs.fastlane.tools/codesigning/getting-started/#using-cert-and-sigh)
to be able to fetch the provisioning profile and certificates on demand, the
`FASTLANE_USER` and `FASTLANE_PASSWORD` variables must be set. You can read more
about this [here](https://docs.fastlane.tools/best-practices/continuous-integration/#environment-variables-to-set).
You may not need these if you are using some other approach to signing.

## In closing...

Remember, you can see a working project with all of this set up by heading over
to my [simple demo app](https://gitlab.com/jlenny/flappyokr).

Hopefully this has been helpful and has inspired you to get iOS builds and
publishing working within your GitLab project. There is some good additional
William Chia's avatar
William Chia committed
[CI/CD best-practice](https://docs.fastlane.tools/best-practices/continuous-integration/)
257 258 259 260 261 262 263 264 265 266 267 268 269
documentation for _fastlane_ if you get stuck anywhere,
and you could also consider using the `CI_BUILD_ID` (which increments each build)
to [automatically increment a version](https://docs.fastlane.tools/best-practices/continuous-integration/gitlab/#auto-incremented-build-number).

Another great capability of _fastlane_ to try is the ability to
[automatically generate screenshots](https://docs.fastlane.tools/getting-started/ios/screenshots/)
for the App Store – it's just as easy to set up as the rest of this has been.

We'd love to hear in the comments how this is working for you, as well as your
ideas for how we can make GitLab a better place to do iOS development in general.

Photo by [eleven_x](https://unsplash.com/@eleven_x) on [Unsplash](https://unsplash.com/photos/lwaw_DL09S4)
{: .note}