If you have a continuous integration server, you might want to build your app with unique version numbers tied to the build.

The agvtool (Apple-generic versioning tool for Xcode projects) is an easy way to update the marketing and build version number within your app.

In your CI system, you can run something like the following to add a build version number where the major version is the same but the .jenkins.$BUILD_NUMBER is appended. $BUILD_NUMBER is provided by Jenkins.

MARKETING_VERSION=$(agvtool what-marketing-version -terse1); agvtool new-version -all $MARKETING_VERSION.jenkins.$BUILD_NUMBER

If you have an enterprise iOS account, you can distribute apps internally using an enterprise distribution certificate. Here are some instructions on how to get an installable IPA via shell commands which you can automate as part of a build process.

You need to have your code signing production certificate downloaded and installed into your Keychain on your build machine. Xcode can do this, or you can download the certificate from the Apple Developer site.

Also, you need to have a mobile provisioning profile installed. The mobile provisioning’s “App ID” should be a prefix (say com.example.*) of your app’s bundle ID (such as com.example.LoremIpsum). Again, you can download via Xcode or via the developer site.

Gather (Optional) Information

While the code signing and provisioning information can be set in the Xcode project’s build configuration, you may want to override the information in your build.

Code Sign Identity

If you need to specify your code signing identity during the build, find your certificate in the Keychain and get the Common Name of the certificate.

The production certificate’s Common Name will be the CODE_SIGN_IDENTITY value.

Mobile Provisioning Profile

If you downloaded the mobile provisioning file, you can open the provisioning profile and find the UUID value in the plist.

If you have downloaded the profile in Xcode, you can go back to Xocde and open the Accounts panel in the Xcode Preferences, look at your logged in Apple ID and View Details, and then find your Provisioning Profile. Right click on the name of the Provisioning Profile and click Show in Finder. You will see a file selected in ~/Library/MobileDevice/Provisioning Profiles. The filename is usually named <UUID>.mobileprovision, or you can verify by opening up the file and find the UUID value.

The UUID will be the PROVISIONING_PROFILE value.

Build an xcarchive

Assuming that LoremIpsum is your Xcode project, you can run the following in your shell:

export BUILD_DIR=$(pwd)/build

mkdir -p $BUILD_DIR

xcrun xcodebuild -project LoremIpsum.xcodeproj -scheme LoremIpsum clean

xcrun xcodebuild CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" PROVISIONING_PROFILE="$PROVISIONING_PROFILE" -project LoremIpsum.xcodeproj -scheme LoremIpsum archive -archivePath LorenIpsum.xcarchive

You now have an Xcode archive which is similar to what is submitted to the App Store.

Export your app from xcarchive

The export archive option in xcodebuild allows a few options which will be saved in a plist file. The $TEAM_ID is the prefix in the App IDs in the Apple Developer portal. It can also be found in the mobile provisioning profile file as TeamIdentifier.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

The above options compile the Bitcode enabled app and add all of the resources into the app (instead of having on-demand resources).

Then just run:

xcrun xcodebuild -exportArchive -archivePath LorenIpsum.xcarchive -exportPath $BUILD_DIR/ipa -exportOptionsPlist $PATH_TO_PLIST_FILE

You should finally have your app’s IPA in the $BUILD_DIR/ipa directory which you can install by dragging and dropping the IPA to iTunes and then syncing your device with iTunes.

One of the tools I’ve been using lately is Packer, a machine building tool. It builds machine images for VirtualBox, VMWare Fusion, OpenStack, AWS, DigitalOcean, Docker, and more,

It can bootstrap from an ISO or an existing image (for some cloud providers and from desktop virtual machine images). You can use shell scripts, Chef, Puppet and other provisioning tools.

Images seem like old school. Afterall, everything is being Dockerized. Containerized applications are pretty amazing and Docker is pretty good in concept. Like git, the raw speed and workflow make it leap over virtual machines. Performance (whether that be the raw processing power or the quickness of the workflow) enables faster feedback which developers always crave. DockerHub is also important.

While Docker is hyped, “raw” machine images are still important. You still need to bootstrap your infrastructure’s base machines. I can have a machine image that runs Docker but there’s still many things that need to be setup on the Docker host. Firewall rules, service discovery, monitoring tools, and so forth are still required outside a container on the host machine.

Building raw images using Packer makes the process repeatable while also making the image available on different possible platforms. There may be a few minor tweaks that need to be made for each platform but in general, I’ve been able to get a single Packer config working on multiple platforms.

I recommend all your software’s system requirements, caching systems, system updates, Docker, etc. to be setup using Packer. Packer allows you to control which version of the system requirements you really want to use.

Once you have Packer build your image, then you can use Vagrant, Terraform, or other infrastructure provisioning tools to do “the final provisioning leg”. The final leg would involve deploying the actual code and doing the final configurations for your services on that machine.

Whether it be building “simpler” and more straightforward images for demos/tests in a CICD process or creating the building blocks of a multi-node infrastructure, Packer is a good tool for repeatable machine image builds.