A Stupid-Simple Automated iOS Build Script

I’ve worked with a bunch of different automated iOS build systems over the years at the various companies I’ve worked for and with my own apps. In the early days of the App Store, many of these were completely home grown. As the toolchain matured, I’ve dealt with Xcode bots as well as dedicated SaaS companies that provide build farms like Microsoft App Center and Bitrise. I’ve also had the horrible misfortune of being tasked with maintaining a dilapidated, Frankenstein of a Jenkins installation that talked to an underpowered Mac mini over a shoddy VPN connection.

What I’ve learned from all those setups is that as useful as they are, they’re generally a bitch to maintain once they reach even a moderate level of complexity. So I tend to shy away from them until there’s a real need.

Over the last few weeks at my current job, that need has presented itself in two ways.

  1. We added a watchOS target to an existing app already in the App Store. For reasons I don’t completely understand even after hours of debugging, it completely broke my co-worker’s ability to submit builds to App Store Connect. We did a fresh clone of the project, blew away Derived Data, deleted and reinstalled every certificate and provisioning profile. Nothing worked until it seemingly fixed itself about a week later for no apparent reason.
  2. For reasons I don’t want to (and can’t) really go into, we have about fifty WiFi networks broadcasting through our small office space. Many of them physically moving around at different times. That, plus what we think is a shitty Comcast modem, means the WiFi we actually connect to randomly fluctuates between passable, to mostly broken, to everyone just gives up and tethers to their phone. The result being that it can take multiple tries and multiple hours to successfully upload our 250MB .ipa to App Store Connect – if it even works at all.

The solution to these two problems? An automatic, repeatable way to produce builds located somewhere else with a good network connection.

Like at many companies, our executives don’t want to use a 3rd-party build service because they don’t want our source code in someone else’s control. So that meant we needed to build something ourselves. And while we may eventually pony up for a hosted Mac mini somewhere, for now during this just-get-it-working-phase, I decided to go the pragmatic route and setup a build system on my (mostly idle) iMac Pro at home that sits behind a very nice Comcast Business connection.

I’m not a DevOps expert. And I’m certainly not an expert when it comes to the thousands of arcane Xcode build settings. But over the years I’ve become very good at diagnosing code signing issues and scripting various bit and bobs together on macOS.

So I spent a couple nights piecing together a straight-forward, stupid-simple, build script that does exactly the minimum necessary to accomplish our goals. Those being 1) the ability to execute a reproducible build on-demand, and 2) automatically build, sign, and submit to Apple on every commit to a specific release branch.

The result is this GitHub project. It’s a single bash script and works exactly the way my own brain expects tools of this nature to work.

For a manual build, you pass the script a JSON file containing various build settings, and it builds the project and then (optionally) submits to Apple.

For automatic builds, I’ve included a sample launchd .plist that checks for new commits every minute and, if any are found, kicks off the build process.

Oh, and as the build progresses through all the various steps, the script can optionally update you with its progress in the Slack channel of your choosing. Even better – and I’m quite proud of this – if an error occurs, it will post the full stdout and stderr log files as Slack attachments so the whole team can immediately debug and see what went wrong without having to SSH into a remote build server.

I think the whole setup is really great. It suits our needs perfectly. I make no claims about it being the right choice for your situation, or that I even did anything remotely unique / interesting when I pieced it together. There are a thousand build scripts out there; this one happens to be mine. I guess what I’m saying is please don’t make fun of my shell scripting abilities.

Check out the README for more details and usage instructions.

More Apple Photos Fuckery

Back in May I posted a Twitter rant about how iCloud Photos was fucking up videos I shot on my phone after upgrading to iOS 12.3. I’m happy to report that hasn’t happened again. But now I’m running into this…

Every two months I upload a bunch of photos to Shutterfly and mail physical prints of my kids to my 95 year-old grandmother – their great-grandmother. The Shutterfly app for iOS makes this very easy. Just select the photos from your library, wait a few minutes for them to upload, and then tap on my grandmother’s saved mailing address and hit send. The whole process takes about five minutes and it brings her a whole lotta joy.

The issue I’m running into is that I’m only able to choose photos from my library. Because I’m doing this on my phone and because Apple doesn’t understand that families might want to see each other’s photos, I can’t pick any pictures my wife may have taken of our kids.

But, as I’ve written about, all of the photos we both take are stored in Google Photos. Great. So today I went to Google Photos, selected about sixty of the best shots, and downloaded them to my Mac.

The next obvious step? Upload those to Shutterfly and ship them off to my grandmother.

Except that Shutterfly’s website uploader doesn’t work in Safari. Like everyone else doing “modern” web development these days, I’m sure they’re only testing in Chrome. Fine.

No big deal. I’ll just import the photos into an album in Photos.app on my Mac, which will then sync to my iPhone, and then I can use the Shutterfly app.

And here’s where Apple’s latest Photos.app fuckery comes into play…

That’s right. I dragged photos from Finder into an empty album in Photos.app and watched as they were imported and then subsequently deleted.

I did this five times with the same result before I finally thought to record the whole fiasco for posterity. No matter whether I dragged from Finder or used the “Import…” menu item – same thing. I even checked the special “Imports” album in the Photos.app sidebar, which shows the most recent group of photos you’ve imported. Nope. Not there either.

Look. It’s not all bad news. iCloud’s shared photo streams? Those are rock-solid and amazing. Our family and friends hardly ever post to Facebook or Instagram anymore. We almost exclusively share through Apple’s shared albums. And like someone recently remarked on Twitter, the photos/videos we share get way more meaningful engagement (likes + comments) from our close circle of friends and family than they ever did via Facebook.

But jesus fucking christ, Apple. Have you really gone so all-in on “iOS is the future” that you’ve abandoned Mac Q/A? Is there anyone on the executive team that actually uses a Mac as their daily driver? I get the feeling that the only reason iCloud shared albums work as well as they do is because they happen to also be a major feature of iOS. But Photos.app on Mac? Or Messages.app on Mac? Pffft. Fuck that. iPad OS is going to have multiple windows soon. Who needs a Mac?