Automated builds from Unity3d to iOS (with OTA deployment)

If you never heard of automated builds before, here is a short description of what they can do for you.

Without automated builds, testing a game on a mobile device looks like this for me:

  • make changes in Unity3d and save them on my development machine
  • commit those changes to my version control system
  • go to / screenshare to my mac
  • check out the changes on the mac
  • open the unity3d project on the mac
  • build the unity3d project
  • open the resulting xcode project in xcode
  • plug in my mobile device, build the xcode project and send the result to my device

As you can guess, this takes quite some time. With the help of automated builds, this is what my workflow looks now:

  • make changes in Unity3d and save them on my development machine
  • commit those changes to my version control system
  • after my build machine sends me an email: open the email on my mobile device
  • click "install" on my mobile device and test the app

In the time between version control commit and the email from my build machine i can keep working on my game and don't need to worry about building anymore. This is pretty great!

So what do we need for automated iOS builds?

  • a version control system (can be perforce, git, svn, you name it)
  • a build machine (in our case it has to be a mac since we want ios builds - windows works fine if you want pc builds for example
  • TeamCity - the sofware for out automated builds. good news: it's free!
  • Xcode - can be installed on every mac for free as well
  • iOS developer membership - this may change soon, but for this tutorial we need it for code signing

Side note: In theory, the build machine can also be the machine you develop on. You will notice performance drops during the build time though. 

Step 1 - setup

This step is pretty straight forward. Download and install TeamCity, install Xcode and make sure that your CodeSign elements are setup correctly.

TeamCity has great tutorials for the initial setup. Follow those until you are ready to set up your first project in it.

Step 2 - setup version control in TeamCity

tut1.png

As the first step the Version Control Settings need to be entered.

First select the type of VCS and then follow the necessary steps. TeamCity tells you when the settings seem to be ok. 

Step 3 - build steps

I divided the build process into 3 separated steps.

  • building the Unity3d project
  • building the Xcode project
  • creating an IPA and deploy it over the air

Step 3.a - build the Unity3d project

Add a build step in TeamCity and choose Command Line from the Runner Type drop down menu. Also choose Custom Script in the Run dropdown menu.

The content of the build script is:

/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -quit -projectPath %teamcity.build.checkoutDir%/path/in/checkout -executeMethod CommandLineBuild.buildIOS -logFile log.log

Let's break this command down into smaller parts:

  • "/Applications/Unity/Unity.app/Contents/MacOS/Unity" - this is the location of your Unity executable. Note that you do not link to Unity.app itself but to the executable inside the app container
  • "-batchmode" - this tells Unity3d that this is a build where no human interaction is possible. There will be e.g. no popups.
  • "-quit" - quit after we are finished
  • "-projectPath %teamcity.build.checkoutDir%/path/in/checkout" - we need to tell Unity3d the path of the project we want to build. %teamcity.build.checkoutDir% links to the directory in which TeamCity checks out the project from the Version Control system you specified. You can add a deeper path afterwards if your project is not in the VCS root directory.
  • "-logFile log.log" - this generates a log file. Useful when something goes wrong
  • "-executeMethod CommandLineBuild.buildIOS" - this is the method we execute in our Unity3d project. I will explain this in more detail below

The first step should look like this in TeamCity:

In order to execute this step without build errors we need to look at -executeMethod CommandLineBuild.buildIOS again. This points to a method in a class that needs to be part of the Unity3d project.

In this example the class is called CommandLineBuild and contains a static method called buildIOS. Important: the class needs to be placed in the Editor folder of your project. Otherwise you get compiler errors.

The source code of this class looks like this:

using System.Collections.Generic;
using UnityEditor;


public class CommandLineBuild
{
    static private string[] collectBuildScenes()
    {
        var scenes = new List<string>();

        foreach (var scene in EditorBuildSettings.scenes)
        {
            if (scene == null)
                continue;
            if (scene.enabled)
                scenes.Add(scene.path);
        }
        return scenes.ToArray();
    }

    [MenuItem(@"Build/BuildIOS")]
    static public void buildIOS()
    {
        var scenes = collectBuildScenes();
        BuildPipeline.BuildPlayer(scenes, "ios", BuildTarget.iOS, BuildOptions.None);
    }
}

I break it down into a few pieces again:

  • [MenuItem(@"Build/BuildIOS")] - this is not required but it helps us to test the script in Unity3d rather than running into compile errors later on.
  • var scenes = collectBuildScenes(); - we need to find and store all scenes from our project before we can build it. this method does it for us.
  • BuildPipeline.BuildPlayer(scenes, "ios", BuildTarget.iOS, BuildOptions.None); - this starts the actual build process. We provide the collected scenes, tell the builder to save everything into a folder named ios and also that we want an iOS build.

The result of this first TeamCity step should be an Xcode project. We handle that in the next step.

Step 3.b - building the Xcode project

Add another build step to your TeamCity project. Again use Command Line as runner type and Custom Script in the Run menu.

The content of the script is:

xcodebuild -project %teamcity.build.checkoutDir%/path/in/checkout/ios/Unity-iPhone.xcodeproj -target "Unity-iPhone" -configuration Release clean build DEPLOYMENT_POSTPROCESSING=YES CODE_SIGN_IDENTITY="iPhone Developer: Your Name"
  • xcodebuild - that is the commandline instruction for building Xcode projects
  • -project %teamcity.build.checkoutDir%/path/in/checkout/ios/Unity-iPhone.xcodeproj - we need to tell Xcode where our project is located. Note that this is the path from the previous step PLUS the ios subfolder in which we built our Xcode project.
  • -target "Unity-iPhone" -configuration Release clean build DEPLOYMENT_POSTPROCESSING=YES 
    this tells Xcode the target platform and some additional configuration parameters
  • CODE_SIGN_IDENTITY="iPhone Developer: Your Name" - this is important for deployment later. the project needs to be signed with your developer identity. Make sure that your code sign certificates work on the build machine before you work on this step. 

The result of this step is an .app version of your Xcode project.

Step 3.c - creating an IPA

Add another build step to your TeamCity project. Again use Command Line as runner type and Custom Script in the Run menu.

The content of the script is:

/usr/bin/xcrun -sdk iphoneos PackageApplication -v %teamcity.build.checkoutDir%/path/to/checkout/ios/build/Release-iphoneos/ProductName.app -o %teamcity.build.checkoutDir%/path/to/checkout/ios/build/game.ipa
mv -f %teamcity.build.checkoutDir%/path/to/checkout/ios/build/game.ipa /Users/youruser/Dropbox/Public/latest_version_of_my_app/
  • /usr/bin/xcrun -sdk iphoneos - this is the command to pack everything of your app into an IPA file
  • PackageApplication -v %teamcity.build.checkoutDir%/path/to/checkout/ios/build/Release-iphoneos/ProductName.app -o %teamcity.build.checkoutDir%/path/to/checkout/ios/build/game.ipa
    here we basically specify the where the .app is located and where we want the resulting IPA to be stored
  • mv -f %teamcity.build.checkoutDir%/path/to/checkout/ios/build/game.ipa /Users/youruser/Dropbox/Public/latest_version_of_my_app/ - this moved the resulting IPA to a folder in the Public folder of Dropbox. I will explain later why we do that.

The result of this step is an IPA file. In theory you could copy this file to your device and run it already. But this is another step we can automate! I will explain this a bit later.

The build steps should look like this now:

 

Step 4 - Trigger the whole process

To save even more time, we want to trigger all those build steps automatically every time we commit a change to our VCS.

This can be done by adding a VCS trigger to the TeamCity project and looks like this:

Step 5 - Over the air deployment

The last step frees us from using cables to transfer the IPA to our mobile device. The cheapest method for this is using Dropbox.

In step 3.c we moved the IPA file to a subfolder in our Public Dropbox folder. We need to add two more files to this folder in order to make everything work.

Create a file called manifest.plist and place it in the same folder as your IPA. Someone was kind enough to put a template for the file on https://gist.github.com/1051160 

  • get the public link to your IPA file and replace the values in line 14 of the manifest.plist
  • if you want to, you can change the other values as well. this is optional though.

Create a file called index.html in the same folder as your IPA and your manifest file. On https://gist.github.com/1051163 someone uploaded a template for this file as well.

  • get the public link to your manifest.plist file and replace the url in line 8 in index.html

Now you can open the public link to your index.html file on your mobile phone and install the app with just one click!

Step 6 - add notifications in TeamCity

Automated builds take some time, depending on your project size and how fast your build machine can build everything. To work around that issue, TeamCity can send E-Mails once a build is finished. This can be enabled in Administration > Email Notifier. After you get the mail, open up your index.html and install the latest version. 

Last addition: it is smart to bookmark the link to the index.html on your mobile device home screen. This way you can access it with on tap.

 

Outlook

This article only covers the most basic steps to automated builds and OTA deployment. There is a lot more you can do with it obviously. With just a bit more scripting you can e.g. save all your different IPA versions and manage them in a list instead of only overwriting the latest version all the time. Or you can add more build steps to build iOS AND android AND windows phone in one automated step. Or... many other things :)

 

I hope this article brings a bit of light into a topic that is dreaded by many small developers because it looks very complicated when you start reading about it. If you have questions contact me on twitter ( @iossif ) anytime.