Tales From An Unchecked Mind

A Blog By Daniel Kennett

Secret Diary of a Side Project: Perception Shift

Secret Diary of a Side Project is a series of posts documenting my journey as I take an app from side project to a full-fledged for-pay product. You can find the introduction to this series of posts here.

In this post, I’m going to talk about some of the mental shifts you need to do when taking the leap from side project to real product.

~

That pivotal moment. It’s weird. You’ve been looking at this project for months, maybe years, slowly adding code here and there. Features slowly form, and the bugs get less “Oh, don’t push that button — it’ll crash” and more “Yeah, I need to make sure those buttons line up”.

Then, one day, you’ll use it for real. In my case, it was in a photography studio learning about portrait lighting. I was taking a break and wanted to look at some of the pictures I’d taken. I happened to have my iPad with me, so I fired up whatever build of the app I had on there and used it to download some photos from my camera. Then, we passed my iPad around and discussed the merits of my photos and how to improve them.

I read or heard or saw a piece by Wil Shipley a while ago (long enough for the memory to have faded a bit, so apologies if I’m mis-quoting him), where he said he takes “Is that all it does?!” as a compliment, as it means that you’ve made something that ‘clicks’ with the user so well that they can just get on with their task without all the stupid computer things getting in the way.

I had that big-style on that afternoon in the studio. At that point, the app was around 15,000 lines of code, which isn’t tiny to say the least — the main breadwinner from my previous company was 18,000. From a programming point of view what I did is pretty complex task — detecting the camera on the network using their stupid thing that isn’t Bonjour, connecting and handshaking with it, dealing with its finicky protocol, downloading a stupid Canon RAW file to the iPad, converting it to JPG and allowing the user to view it fullscreen.

Downloading photos from a WiFi-enabled SLR in an early build of my app.

My app distilled that down to two taps. Three, if you count launching it in the first place. And in this busy place with lots of people wanting to see my picture, it worked flawlessly. I launched the app and it found and connected to my camera and presented the photos within. “Can I see that one?” someone said. I tapped the “Download” icon, watched a progress bar for a few seconds, then tapped the thumbnail that appeared in the “On this Device” pane to view it fullscreen. “Cool!” the guy said “…but I think if you moved the light down a bit, you’d make that shadow softer, which I think would make the photo look nicer.”

At that moment, I felt so proud. This is why I make software. The process was so smooth that it completely melted away into the background, to the point where nobody else even noticed the app. Instead of spending time using my app, we spent time learning about studio lighting.

Evidently I missed the “Try not to look like a scruffy hobo” preparation step of going to a studio.

That day, my project shed its “side project” status and became a thing I knew could be genuinely useful to people. I’d been idly considering it for a long time, but I knew that I should try to finish this and ship it.

~

Once this big perception shift has happened, everything changes. It’s really important to take this perception shift seriously and be strict with yourself — if you carry on as before, your side project will stay a side project and you’ll end up shipping something not up-to-scratch.

Before we continue, let’s take a moment to define what a side project actually is:

  1. The primary attribute that all the others derive from is that it’s a project written in your spare time, which gives extreme time limitations.
  2. Features are typically written on the path of least resistance, throwing best practices out of the window in the name of getting a feature done in the hour you have before you go to bed.
  3. Unit tests? Nah. Error checking? // TODO: Handle this properly

Obviously, these behaviours need to change. So, how do we manage the transition?

Keep Refactoring Strict

As soon as you decide to turn a side project into a real product, it’s very tempting to go back and refactor everything immediately. Rebuild the code! Undo all the shortcuts! Add tests! But, if it works… don’t. Not yet.

You need to be really strict about rebuilding if you ever want to ship the thing. The rule of thumb I follow is: If I ship a build to testers with no changes except this refactor, will they notice? Sometimes the answer will be “yes”, and if that’s really the case then go ahead. However, if the answer is “no”, think hard before touching that code.

An example of this is the library that actually speaks to the camera in my app. It has three layers — the outer layer defines a very generic API for the application to consume. “Take a picture please”, “List the files on the memory card”, etc. The middle layer defines slightly lower-down APIs, but still fairly generic. “Send this command with these parameters, then give me the response here”-type stuff. Basically RPC. Finally, the very-internal layer actually deals with the grunt work of encoding the binary messages properly, handling responses and dealing with the upkeep of keeping the camera happy.

These three layers work in concert so the application doesn’t have to care about the workings of the camera at all. I’m actually pretty pleased with the design of most of it.

However, that inner layer. The most important one. The most intricate one, dealing with TCP sockets and threads and all that fun stuff. It’s terrible from a code-style point-of-view.

However! It works, and it works well. The last commit on that class was in June 2014, adding a minor feature. Before that, I last touched the code in July 2013!

I must’ve been severely sleep-deprived when I wrote this glorious mess. But, it works, so it stays.

As an engineer, I would love to refactor that class. I could redesign the shit out of it — it’d be a work of art! However, by now that class must have handled over a million commands to and from cameras in my care, and it just plain works. That code is over a year and a half old, and now this project is a real product with a release looming in the not-too-distant future, it just doesn’t make sense to mess with it.

…and Do It Right When You Do

Alright, so it’s time to refactor some code from the side project era. Make sure you do it right — design it properly, add tests, etc. Replacing some old crappy code with slightly less crappy code that hasn’t been thought through or tested properly isn’t worth the effort.

A Commit A Day…

I got this tip from Simon Wolf: do something on your project every single day, no matter how big or small. I started doing this and bar my honeymoon, I’ve kept up with it pretty well. Sometimes (like today) all I do is tweak the in-progress website a bit. Yesterday I moved my icons from Photoshop to Sketch. The day before, I spent nearly 11 hours working on an advanced feature.

Every time you touch your project in any way, unless you’re doing it very wrong, it’s getting better. It might only be a tiny bit better, but it’s getting better.

Over a year’s worth of tiny bit betters and my project is pretty damn mature.

Keep Away From New Shiny

Remember: This is a real product now. If you bring in every shiny new library or even language that comes along, you’ll end up spending hours working and not moving forward at best, and end up with a half-working project that has hundreds of dependencies at worst.

This Is Starting To Feel Like Work!

These restrictions take away a lot of the fun things about side projects. This can be a little discouraging at first, but stick with it. You’ll (hopefully) start to find joy in the results of your work rather than the process of making it. Sure, mucking around with autolayout for hours is a pain in the ass, but just look at that UI! It’s glorious!

~

Next time on Secret Diary of a Side Project, we’ll talk about some actual software development practices and idioms that’ve helped me. There’ll be code and everything!

Compile-Time Image Name Checking

In my continued quest to banish as many string literals as I possibly can from my project, I wrote a little tool to generate a header file containing string constants for the names of all the images in my .xcassets bundle.

This means you get compile-time checking of two things — you misspelling the image name, and images being named incorrectly.

The tool is very simple, and is mostly a duplicate of the generate-string-symbols tool I wrote a while ago. It runs through your .xcassets directory and writes the found image names out to a header file. You can see the source over on GitHub.

For consistency’s sake, you can pass -prefix and -suffix parameters to the tool, which do what they say on the tin. A prefix of CBL and a suffix of ImageName will convert and image named Awesome into CBLAwesomeImageName, with a value of @"Awesome".

Putting It All Together

To integrate this into your project, there are three steps:

  1. Generating the header files when your project builds.
  2. Telling Xcode where to find the generated files at build time.
  3. Importing the generated header files so you can use them.

First, you want to create a custom build step in Xcode before the Compile Sources build step to generate header files from your image assets. My custom build step looks like this:

1
2
3
4
5
"$PROJECT_DIR/Vendor/generate-imageasset-symbols/generate-imageasset-symbols" 
    -assets "$PROJECT_DIR/Cascable/Images.xcassets"
    -out "$BUILT_PRODUCTS_DIR/include/CBLImageNames.h"
    -prefix CBL
    -suffix ImageName

It uses /bin/sh as its shell, and I have the generate-imageasset-symbols binary in the Vendor/generate-imageasset-symbols directory of my project. It places the generated header file in the include directory of the build directory.

Next, you need to tell Xcode where to search for your files. Make sure your project’s Header Search Paths setting contains $BUILD_PRODUCTS_DIR/include.

At this point, you can start using the symbols in your project. However, you’ll need to #import your generated header files(s) in each file you want to use localised strings in.

To get around this, can #import them in your project’s prefix header file.

1
2
3
#import <CBLImageNames.h> // Generated from image assets

UIImage *image = [UIImage imageNamed:CBLAwesomeImageName];

…and you’re done! You can find the generate-imageasset-symbols project over on GitHub under a BSD license. Enjoy!

The Antisocial Network

I have a love-hate relationship with Facebook. I opened my first account after my girlfriend (since fiancée, now wife) wanted to tag me in stuff she did. I survived maybe a couple of years before getting bored and deleting my account.

I opened a new account in 2010 to organise a leaving party when we moved to Sweden. I’ve kept that account to this day.

Starting on a positive note, I enjoy that 99% of my friends are on Facebook since it makes organising events super easy. Their Messenger app isn’t that bad either.

However, my Facebook timeline is absolute trash. I wrote about it a little in my Mental Health post last year, and it’s summed up beautifully in the short (~2:30) film What’s on your mind?, but the lives I’m seeing in tiny, out of context snippets through my Facebook timeline are completely false. Hell, I’m guilty of it myself.

The thing is, I know the people in my Facebook timeline are nice people. Sure, I unsubscribed from some friends long ago since they were just spewing out nothing but “funny” pictures and KEEP CALM AND _____ ON slogans, but by-and-large my friends are lovely people. I wouldn’t be friends with them otherwise!

Recently it was getting to the point where I was starting to dislike people I know I really like, even childhood friends I’ve known for decades.

“Why don’t you just unfollow them?”, my wife asked.

So I did. I unfollowed every single person I’m friends with. My Facebook timeline is now completely empty.

Bliss.

The obvious question is “Why don’t you just delete your account?”. Well, since 99% of my friends are on there, I can still chat with them directly and organise events with them, and Facebook is a decent tool for that.

I’m hoping that not receiving a constant stream of tiny snippets will make me think “I wonder what x is up to these days?” sometimes, ending up contacting that person “properly” via some sort of two-way direct chat (IM/phone/whatever). I haven’t spoken to some of these people for over a year, but Facebook has tricked me into thinking that we’re in contact because I see their face on a timeline once in a while, and they see my face in theirs.

Somehow, a social network has made me an unsocial friend.

Secret Diary of a Side Project: Introduction

Nearly ten years ago, I founded KennettNet Software Limited. It was a small software company that came into being after a couple of years of making Mac OS X software on my own led to an income that was enough to support me full-time. Ostensibly, the company was successful — it had four employees at its peak, and its failure was, at its core, due to my being a poor businessman.

This was at a simpler time, when making applications for Apple platforms was a niche thing to do. The iPhone was non-existent, and mentions of Apple in the press were almost always accompanied with the word “beleaguered” right there in the headline. You could make a living writing Mac applications, but there was no gold rush — and, more importantly, no rush to the bottom either.

When I shut the company down in early 2012, it was still earning enough to support me if I went it alone with a clean slate. Unfortunately, the sum of the company’s liabilities and my own debts far out-shadowed the company’s income, so it had to go. It was a bittersweet moment — it was sad to shut my company down, but it released a lot from my shoulders and I was able to repay all of my debts over the next couple of years.

I had a sign! A real one!

Now it’s almost 2015 and I have that clean slate, and all I want to do is try again at indie development — it’s in the fibre of my being. Despite the latter years of KennettNet being nothing but pain, I still remember the whole experience as one of the best times of my life.

I’ve been trying to fight it for over a year — after all, I have a steady income from a great company that’s been more than accommodating in letting me work on projects that are rewarding to work on (I got to go to Cupertino and work inside Apple for a couple of days on a then top-secret project — how cool is that?). From all logical points of view, it’d be crazy to even think about leaving.

Times have changed. It’s hard to do things the “old way” — that is, to make an app, sell it for money and be able to put a roof over your head, and there’s been plenty of documentation of this fact recently.

I could try the “new way”, of course. We’re still (just about) in the gold rush phase of iOS development. I could half-implement an idea then convince a VC company to give me millions of dollars to finish it. I could then hire some people and spend the next six months trying to figure out how to make those millions of dollars back, pivoting several times until the product we’re working on no longer resembles the original vision and so scared to charge customers money that we never exit beta and end up with an ad-riddled service that nobody likes.

The “new way” isn’t my style, as you can probably tell from the bitterness of the above paragraph. It just isn’t. It works for some people, and more power to them. However, I’ve spent enough time in the Silicon Valley bubble — or at least at the edge of it looking in — watching companies solve problems for wealthy, middle-class white people living in suburbia to turn me off that area of the industry for life.

I’ve been working on a side project for over a year and a half now, and you know what? At some point it went from a hacky side project to a genuinely useful app that’s just not ready for prime-time yet.

My project’s agile board in 2013.

It’s time to throw caution to the wind — in a careful and well-planned manner, of course. March 12th, 2015 will be my last day at Spotify for a little over six months, in which time I’ll be working on my app full time in an effort to release it the “old way” — I’ll charge money for it in an attempt to earn a living. It’ll no longer be a side project, it’ll be my full-time work, funded by my savings account.

Why the 12th? Well, on March 13th, my first day off, I’ll hop on a plane and fly over to the UK to attend NSConference — a community-focused event that always inspires me. It’s a fitting start to an exciting time, I feel.

This decision hasn’t come lightly. I’ve been fighting it for over a year, but I recently realised that if I don’t do it now while the opportunity is available, I’ll regret it for the rest of my life.

So, what’s Secret Diary of a Side Project? In the coming weeks and months as we approach March 12th, I’ll post a series of articles on this blog discussing the project — the disciplines I’ve used to keep it moving forwards, the lessons I’ve learned along the way, and other various tidbits about making an app as a “spare-time” indie developer.

In all likelihood, all I’ll achieve is proving Brent, Marco, Gus and a number of other developers correct in their assertions that you can’t make good money as an Indie developer on the App Store these days. However, I’m going to have a lot of fun doing so and you, dear reader, get to come along for the ride.

I’m really, really excited about this. The project is in an area I’m passionate about (photography) and at the very least, I’ll get to make an app I’m proud of. I’m also looking forward to re-engaging with the Cocoa development community — something I’ve not been able to keep up with as much as I’d like with a corporate job.

As always, feel free to keep in touch with me via @iKenndac on Twitter.

Mental Health

Until today, this story has been shared with four people in the world. My wife, my manager (who only knows the parts of the story he actually took part in), my friend Tim and my psychiatrist.

Recent events in both my private life and the world at large have convinced me that discussing mental health can only be a good thing. If you only visit here for posts about programming and stuff, you’re welcome to give this a miss. However, I’d encourage you to give it a read — who knows, it may help you or someone you know.

~

There’s nothing quite like writing a letter demanding a solicitors release money from your dead Mother’s estate to make you feel like the scummiest person on Earth.

This is the pivotal moment that poisoned my mind for six months, damaging a time that should otherwise be only filled with wonderful memories — those of marrying my wife and buying a house together.

Grieving the loss of a parent is always going to be a shitty part of your life no matter what. Not to go all Batman about it, but my father died when I was younger and it’s a process I’ve been through before, albeit with a much less mature mind at fifteen years old. This time around I was doing fine, all things considered, and had settled into some sense of peace once I realised (amongst other things) that since death is a fact of life, a parent passing before their child is orders of magnitude less of a tragedy than vice versa. Yes, it’d be lovely if nobody had to die, but that’s not how life works.

However, being her closest living relative, I had the task of supporting the solicitors assigned to administer my late Mother’s estate. In May 2013 I sat in their stuffy office in Stevenage in the UK and was assured that the estate should be a fairly simple affair and that they couldn’t see it taking more than six months to complete. Two weeks later they sent me a document that contained a few currency conversions, all of which had been calculated wrong. And so started the most harrowing set of bureaucracy I’ve ever encountered in my life.

~

My breakdown came in a one-on-one meeting with my manager in October 2013. In a previous blog post, I hinted that I was thinking of trying to “go Indie” again, and in October I’d committed to take six months off from March 2014 to try just that. All the paperwork was signed, and I was looking forward to making my own apps “for real” again.

However, the process with the solicitors was still trundling along and having to research the multitudes of laws at play to make sure they weren’t making more mistakes was really dragging me down, and it was starting to affect my productivity at work. I thought it’d be a great idea to move this six month leave from work forward to January so I could combine making the app with taking some time off to recuperate.

The flat “No.” that came out my manager’s mouth hit me like a brick wall. “…I’ve seen you planning this leave for months now, and every time you talk about it your face lights up. If you’re unwell, you should use medical leave to get better, and make sure your time away is kept a happy thing.”

I just kind of sit there in a dumbfounded silence.

“Obviously, the company has no legal grounds to deny this request, so if you’re sure you want to do this I’ll approve it, but I really don’t think you should.”

“…are you alright?”

I realise I’ve been sitting there in silence for what must have been a minute. I can’t see very well, so I take off my glasses to clean them. To my surprise, I realise I’m crying quite heavily.

“Go home. Take a couple of days to catch up on your sleep, then call the doctor and get help. Don’t worry about your work, I’ll cover for you and tell people you have the flu or something.”

“But…”

“Now.”

I get up in silence and scurry to my desk, staring at the floor hoping to God that nobody looks at me. I put on my hoodie and leave the office as fast as I can, tears streaming down my face. I don’t break pace until I’m sitting on the underground train, huddled in the corner with my hood up trying my hardest to be invisible. I’m sure everyone else thinks I’m some unstable lunatic as they avoid sitting near me. Hell, I feel like an unstable lunatic. I’m so upset and so confused that I’m genuinely wondering if I’d had a stroke, or if I’d ever be able to show face at work again.

After that day I do whatever I do when I’m going through a tough time, especially when I can’t rationalise and understand what’s going on — I withdraw into myself. Social interactions become more and more difficult as time draws on, mainly because I feel more and more incapable of functioning in civilised company. Suddenly the answer to “How are you doing?” becomes an elaborate lie, and the friend telling me about how he’s having a great time becomes a stab in the heart. Presenting myself for casual interaction becomes hard work, so I stop doing it.

Even online interaction becomes difficult eventually. You’ll notice a hole in this blog between October 2013 and July 2014 due to this, and while I can’t seem to find any tools to show my social network engagement over time, I’m sure you’ll find that my Twitter and Facebook use during that time dropped off a cliff, too.

Facebook in particular is completely devastating to someone going through a tough time. A quote I hear a lot goes along the lines of “You’re comparing your blooper reel to everyone else’s highlights” and it’s a fairly accurate little soundbite. My Facebook feed, just like everyone else’s, is full of people jostling for attention, desperate to prove they’re winning at life. Look at my amazing holiday! Look at my awesome new phone!

Instasham by Pandyland

Typically, all these posts don’t really bother me — hell, I do the same thing — and I unsubscribe from the people who are particularly obnoxious. In my broken state, however, each one of those posts became a slap in the face, causing me to further distance myself from my friends and coworkers. Eventually, I’d stopped talking to most of the people I knew and had stopped attending any social events at all.

~

Nearly a year later, I realise that manager practically saved my life. I didn’t return to work for well over a week after that incident, and only after doing as instructed — I spoke to my local doctor who referred me to psychiatric care at a clinic in Stockholm.

My psychiatrist (who I’ll call my doctor from now on, since it takes me about two minutes to type ‘psychiatrist’ correctly each time) and I pieced together what had happened. In short:

  • My Mother had died.

  • Being the closest living relative, I had to support the solicitor appointed to deal with my late Mother’s estate.

  • The solicitor was, to put it nicely, requiring a lot of support.

  • Being in a different country to the solicitor made their multi-month gaps in communication all the worse.

All in all, I was having a pretty shitty time. The combination of these factors, though, was poisoning my mind and I couldn’t even see it. Looking back, my correspondence with the solicitor is all like this:

  • One email to the solicitors was discussing that a multiple month delay to “wait for some paperwork” wasn’t acceptable since the paperwork in question was super easy to get, never mind the fact that I had copies of all of it that I’d have been able to supply in ten minutes if they’d only told me what they needed.

  • Another email discussed Section 27 of the UK’s Trustee Act 1925 and the liabilities of the various parties dealing the the deceased’s estate before and after the Section 27 notice was filed.

  • Yet another discussed finding records from my Father’s death and supplying them to the UK Tax Agency to get the correct tax status for the estate.

All of this is completely normal stuff to be discussing with legal representatives during this sort of process. However, the entire time it just felt like I was typing GIVE ME MY MONEY. GIVE ME MY MONEY. GIVE ME MY MONEY. over and over and over again. I felt like the worst human being alive simply for making sure my Mother’s estate was dealt with properly and promptly.

Except I didn’t know that’s why I felt horrible. I just felt like a horrible person and I didn’t know why.

Turning it Around

My doctor listened to my story and the most wonderful thing happened. She became autocomplete for my mind, filling in holes in my head I didn’t even know were there.

I told her about that meeting with my manager in which I completely melted down. “Yes, it can be funny how someone else noticing you’re not doing so well can make you realise how bad you’re actually feeling”, she said. I nodded a slight laugh and replied “Yes, it really brought it home”. Wait, it did? Of course that’s what happened! My mind flooded with a clear memory of that meeting — about how I was touched that my manager was so caring, about how I was really not coping well with the multiple jobs I had to do.

Later, I told her about the constant back-and-forth with the solicitor, and made an offhand remark about feeling “kinda greedy” even though I know, rationally, that this process has to happen no matter who does it. “That sort of thing can really eat at the soul, can’t it?” Of course that’s why I feel so bad!

On the way home, I’m bursting with excitement at finally being able to see my problems, and can’t wait to tell my wife all about it. Destructive Daniel kicks in and I start to feel guilty — my sweet, loving wife has done nothing but stand by me and support me while I turn myself inside out for months, and I end up going somewhere else to get help and am transformed in an hour. I wonder if she’ll feel like she’s not capable of supporting me. I wonder if I’m being selfish.

Of course, my wife is ecstatic that I’m making positive progress and I feel like an idiot.

~

Once we’d rooted out what exactly was wrong, we started a form of CBT which, very simply, is a treatment that embodies “If you were happy when you did x, you should do x.”. My doctor spent a session drawing a diagram on the whiteboard of the destructive cycle that’s common:

First, something crappy happens.

  1. Because you feel crappy, you can feel tired and have the instinct to stay at home and rest and avoid people.

  2. Because you’re at home, you miss out on the things that give you joy — seeing friends, taking part in hobbies, etc.

  3. Because you’re missing out on the things that give you joy, you feel more crappy.

  4. Goto 1.

“So, does any of this seem familiar?”

That session is when psychiatry ‘clicked’ with me and I realised just how powerful it was. Simply by drawing a diagram on a board, my doctor both showed me the negative cycle I was in and how to fix it. I don’t mean to belittle her when I say she “simply” drew a diagram — she drew it in the way I “simply” write a computer program to solve a problem or an engineer “simply” uses arches to build a bridge capable of holding up a desired load. Her years of training and experience allowed her to express a concept completely alien to me in no time at all.

My tasks were to force myself to do things I knew I enjoyed doing, even if I didn’t think I didn’t want to. I started out within the house — practicing guitar, doing stuff with my railway, etc. Once I was able to do that, I focused my efforts on actually leaving the house and attending social events. It took an absolutely herculean effort, but I was able to attend a group of friends’ weekly-ish “Hacktisdag” pizza-and-programming gathering again, if only for a couple of hours to start with. Baby steps.

From then on, I started recovering in leaps and bounds and I was able to pull myself out of the negative cycle with some dedication and help from my wife.

Back To Normality

When I think back to the six month period between late 2013 and early 2014, my mind should fill with happy memories of getting married to the love of my life and moving into a beautiful house together. Instead, my heart fills with a deep dread that the person I became — an introvert consumed by confusion and guilt, being driven away from his friends by his own hyper-destructive interpretation of events — is coming back.

It’s slowly getting better. Occasionally I’ll be taken by surprise as an otherwise innocuous comment manages to sting way more than it should, but those are getting rarer and I’m able to shake them off in a few moments.

I’m very lucky, though. By some absolute miracle, a wonderful manager noticed I was in trouble and led me to an incredible doctor who taught me how to identify when I might be having trouble and the steps I can take to mitigate the problems. I avoided depression. Just. Really, by the skin of my teeth.

When Robin Williams committed suicide, I read the same thought over and over again — what could someone so successful and wealthy possibly be so sad about?

I’m a successful young guy earning good money at a fun job, and I have lots of friends and live with my wife and dog in a beautiful home. I really have nothing to complain about. Yet all it took was a few emails and before I knew it I was so outside of my own mind I didn’t know which way was up. I wound up huddled on an underground train, scared and confused and crying and helpless.

And remember: I wasn’t even depressed.

We don’t hesitate to seek help with our cars, our computers, hell, even the rest of our bodies when they break. Somewhere along the way it became taboo to talk about mental health — a subject reserved for conversations that start with “Can you keep a secret?” or, too often, never start at all.

I really wish this would change. I genuinely believe it could save lives.

Epilogue

Just over a week ago I visited my Mother’s final resting place for the first time since she died — a remote spot on top of a mountain in the Alps near the French-Italian border.

My Mother’s Resting Place, ~2100m Above Sea Level Atop Combe Chauve

After an hour-long descent in a battered old 4x4, I slumped into my Mum’s old sofa and opened my laptop to find an email from the solicitor. They’d finished administering her estate, pending my approval.

That email was sent while I was standing on top of the mountain taking in the beauty of it all, less than five minutes after I took the above photo.

Perhaps I should have gone up there sooner.

Compile-Time NSLocalizedString Key Checking Part 2: Other Languages

In my last post, Compile-Time NSLocalizedString Key Checking, we explored a solution that elevated .strings files from “just another resource” to something that we could get compile-time checks on.

However, these checks only worked on our “master” .strings file — the one strings get added to first, typically in Base.lproj or your local development language.

This becomes a problem sometimes as I’m working in a flow like the one diagrammed below.

Translations take a while to get done – perhaps a week or more. In the meantime, I’m working on other things and sometimes updating the code for the feature that’s getting translated in a way that means the strings need to be updated.

Since this process is very asynchronous, we quite often bump into problems caused by out-of-sync translations while testing. This is normally not too much of an issue, but it’s a waste of everyone’s time if we give a build to a tester in another country and get a report back that some translations are missing.

Since we’re working with huge amounts of strings and with a third-party localisation service that isn’t integrated into Xcode at all, manually diffing .strings files is a pain, and is really a problem that should be dealt with by the computer.

The Solution

A picture tells a thousand words.

verify-string-files is a little tool I wrote (and is available on my GitHub) that emits warnings or errors if a string is present in the “master” .strings file but is missing from any localisations.

Usage is very similar to my tool that generates header files from .strings files, but a bit simpler – it takes a single input file, the “master” file, and automatically finds matching localised files.

To integrate it with your Xcode project, add a custom build step at any sensible point in the build process that runs the following script:

1
2
"$PROJECT_DIR/Vendor/verify-string-files/verify-string-files"
    -master "$PROJECT_DIR/Cascable/Base.lproj/GeneralUI.strings"

It uses /bin/sh as its shell, and I have the verify-string-files binary in the Vendor/verify-string-files directory of my project.

The tool will output log messages if it finds any problems, and Xcode will pick them up and display them just like my screenshot above. If you want the tool to output warnings instead of errors, add the -warning-level warning parameter to verify-string-files — a useful thing to do is have the tool emit warnings when debugging but emit errors if you try to make a release build with missing strings.

You can find the verify-string-files project over on GitHub under a BSD license. Happy localising!

Compile-Time NSLocalizedString Key Checking

There are two typical flows for using NSLocalizedString to localise your application’s strings:

  1. Type the “base” string into your source directly, then use genstrings to generate strings files. In your .m files, your calls look like this: NSLocalizedString(@"Please enter your name:", @"String used to ask user for their name");

  2. Type a unique key into your source, then add the string for that key straight to the strings file. In your .m files, your calls look like this: NSLocalizedString(@"CBLUsernameFieldLabel", nil);

There are various merits to both approaches, and I’m not here to argue which one is best. In my world, at work we use approach #2 because our localisation process kinda requires it, and I use #2 in personal projects because, well, seeing user-facing language in .m files gives me the heebie-jeebies — those files are for computer language, not people language.

This post is mainly for people who use approach #2.

Whither Error Checking?

At least once in your life, you’ll have seen something like this in your projects:

Even worse, if you’re using approach #1, you might not notice the missing localisation until you’ve shipped your product and start getting complaints from customers that half your app is in English and the other half is in Spanish.

The problem is that there’s no compile-time checking of strings files, and while there’s a few debug tools that you can use to spot un-localised strings, in the real world these won’t be run nearly as often as they should.

After extensive research (ten minutes of Googling) and a quick poll of Twitter (which resulted in one suggestion involving grep, and an argument) I couldn’t really find anything like this.

If You Want a Job Doing…

I ended up writing a little tool that takes a .strings file as an input and outputs a header file containing NSString constants for each key in that file. It turns this:

…into this:

Now we have compile-time checking that my keys are present and correct, and we get autocomplete for free. Much better!

The tool is very simple, and is 80% error checking. It reads the keys in using NSPropertyListSerialization and writes the found keys out to a header file. You can see the source over on GitHub.

Putting It All Together

To integrate this into your project, there are three steps:

  1. Generating the header files when your project builds.
  2. Telling Xcode where to find the generated files at build time.
  3. Importing the generate header files so you can use them.

First, you want to create a custom build step in Xcode before the Compile Sources build step to generate header files from your strings files. You could be less lazy than me and create a custom build rule to automatically do this to all your strings files, but I’m lazy. My custom build step looks like this:

1
2
3
"$PROJECT_DIR/Vendor/generate-string-symbols/generate-string-symbols"
    -strings "$PROJECT_DIR/Cascable/Base.lproj/GeneralUI.strings"
    -out "$BUILT_PRODUCTS_DIR/include/GeneralUI.h"

It uses /bin/sh as its shell, and I have the generate-string-symbols binary in the Vendor/generate-string-symbols directory of my project. It places the generated header file in the include directory of the build directory.

Next, you need to tell Xcode where to search for your files. Make sure your project’s Header Search Paths setting contains $BUILD_PRODUCTS_DIR/include.

At this point, you can start using the symbols in your project. However, you’ll need to #import your generated header files(s) in each file you want to use localised strings in.

To get around this, can #import them in your project’s prefix header file.

In my project, I have a “convenience header” which imports the generated files and provides a couple of helper macros to make localisation a little nicer, especially considering I use non-default string table names.

CBLLocalizedString.h
1
2
3
4
5
#import <Foundation/Foundation.h>
#import <GeneralUI.h> // Generated from strings file

#define CBLLocalizedString(x) NSLocalizedStringFromTable(x, @"GeneralUI", @"")
#define CBLLocalizedStringWithFormat(x, ...) [NSString stringWithFormat:CBLLocalizedString(x), __VA_ARGS__]

…and you’re done! You can find the generate-string-symbols project over on GitHub under a BSD license. Enjoy!

Photo Challenge: Time Warp

Over a year ago, I “reviewed” my Canon 6D. I love that camera, and it can take beautiful photos. However, it’s big, especially with the battery grip attached.

This means that I only take it with my when I actively want to take photos, which doesn’t include, well, 95% of my life. Sure, I always have my iPhone with me, which has a great camera for a phone… and until that suffix — for a phone — is gone, it’s not going to cut it as something to record photos that need to last a lifetime. It’s great at photos of landscapes or people where everything is roughly the same brightness, but as soon as you show it something challenging such as a scene with bright areas and dark shadows, the poor dynamic range sticks out like a sore thumb. As a friend put it: “So, let’s summarise: Your device which has no other purpose than to be a camera, is better at being a camera than your phone which is many devices. Who knew?!”

Anyway, my search for a small but high-quality camera led me to Micro Four-Thirds (MFT) cameras, and on to the snappily-titled Olympus OM-D E-M10. I’ve always had a soft spot for Olympus cameras (I still have an Olympus OM-1 which was given to me as a child by my father), and after a bit of deliberation I picked one up.

Once I got it home, I realised just how similar it was to my 1970’s-era OM-1!

Occasionally I pick up that camera and wish I could take pictures with it again. I have fond memories of using that OM-1 when I was younger and the excitement of dropping off my roll of film at the local chemist (followed often by the disappointment of all my photos being terrible). Perhaps it’s all nostalgia, but it’d be fun to take photos like that again.

So, I present to you…

The “Time Warp” Photo Challenge

The idea is to take photos using only the features available to you on a 70’s era SLR like my OM-1. Here’s the OM-1’s feature list:

  • Manually adjustable shutter speed (dial on camera)
  • Manually adjustable aperture (dial on lens)
  • Manually adjustable focus (ring on lens)
  • Adjustable ISO (replace film with film of the desired ISO)
  • Clockwork self-timer
  • Photo capacity of 36 shots with the right film
  • Light meter (literally the only electrically powered thing on the camera)

So, here are the rules of the challenge:

  1. You’re only allowed to take 36 shots.
  2. Your first viewing of the photos must be back at home after you’re done. You’re not allowed to look at them on the camera screen, or delete them.
  3. You have to choose your ISO value at the beginning of each set of 36 photos.
  4. Manual focus only.
  5. Manual exposure controls (shutter speed, aperture value) only.

If you really want to commit, you can modify #2 to be “Your first viewing of the photos must be after getting them printed at a photo store”, but I want this to be fun rather than a complete loss of modern technology.

With some setup, it’s actually pretty easy to simulate these limitations on a modern camera — otherwise it’d be way too easy to “accidentally” steal a glance at the screen after taking a photo, or to go over the photo limit:

  • For #1 I found the smallest memory card I could (2Gb) and filled it up with a big empty file to enforce the 36 photo limit.

  • For #2 I disabled “Photo Review” on my camera, so it doesn’t automatically display the photo on the screen just after it’s taken.

  • #4 was enforced for me by virtue of my old lens.

  • #5 was partly enforced by my old lens, but keeping my camera in M mode isn’t too hard.

The equipment I ended up using:

  • My Olympus OM-D E-M10
  • My old Olympus F-Zuiko 50mm f/1.8 lens
  • A Novoflex MFT-OM lens adapter
  • A 2Gb SD card I found in an old point-and-shoot camera, filled up with an empty 1.4Gb file to only allow 36 photos to be taken

My Results

Note: All photos after this point were taken under the constraints of the challenge, using the equipment mentioned above. Only minimal editing has been done (cropping, slight contrast boost, etc).

I have to admit, I wasn’t optimistic at first. I tried manually focusing with the new lens that came with my OM-D and had a really hard time. The lens is focus-by-wire and very sensitive — I had trouble finely adjusting focus at wider apertures. I tried with some of the lenses on my Canon 6D which weren’t focus-by-wire, but the manual focus ring was still too sensitive for my liking.

Then, I remembered about my old manual lenses for the OM-1 I had in a bag somewhere in the basement. Maybe they’d be better! A decent adapter to put them on my OM-D is an incredibly expensive item considering “it’s just a lump of metal” but I gritted my teeth and made the purchase…

…and what a transformation occurred!

It’s an incredible cliché to say “They don’t make them like they used to!” by holy crap, this… thing, this thirty year old, dusty, dented thing absolutely transformed my dinky little camera into something that felt meaningful. The hyper-sensitive and lifeless manual focus on my modern lens was replaced with a mechanical focus ring that feels beautiful to the touch, gliding purposefully but smoothly under my fingers. Suddenly, focusing manually was a wonderful experience again.

Suddenly, I was excited about this challenge. Time to go take some pictures!

Now, I’ve never been “that guy” — you know, the one that has his camera on high-speed continuous shooting and takes at least six shots of everything he wants to take a picture of, but I’m completely used to having a 32Gb memory card in my camera, allowing for over 1,000 shots before I need to start worrying about free space.

However, having a limitation of 36 photos transforms the experience, especially when combined with the fact I can’t delete or look at the photos I take. Suddenly, the shutter click of the camera becomes the solid thunk of a letterpress machine, a mechanical and meaningful action that you’re acutely aware of. Every photo becomes important.

In the few hours I spent doing this challenge the other day, during an afternoon out driving through a nature reserve to Nyköping, I ended up skipping a ton of photos that I’d normally have taken because they just didn’t feel right.

And you know what? I was loving every minute of it. “I can’t wait to get back home and look at these pictures,” my wife said after I took a photo of her and our dog, Chester. “Don’t look at them before I’m ready!”

Back Home

When getting back home, my wife and I excitedly loaded up Lightroom and imported the pictures. Every single one got a response from at least one of us. Not a single photo was wasted, and apart from a couple where the focus was so bad it ruined the photo, not a single one was deleted.

My computer has over 6Tb of local storage available to it right now, with infinitely more available in “The Cloud”. My $20 memory card allows my camera to take over 1,000 photos at once. And you know what? It’s too much. I have many albums in my digital photo library of trips and holidays I’ve been on that contain hundreds upon hundreds of photos. 99% of them are useless, drowned in a sea of pictures of crap I no longer care about. Sure, that lamppost might’ve looked cool at the time, but three years down the line? Its meaning is completely lost by all the other lampposts and trees and street corners in that album.

I then noticed that all of my favourite photo albums only have a few photos in them due to constraints at the time. The WWDC Photo Walk album, for instance, or the time I went to San Francisco for two days and had a couple of hours to kill before the event started.

Conclusion

I absolutely adored this challenge and will be doing it again, repeatedly, in the future. The combination of a good manual lens and the rest of the constraints added really reconnected me with photography, and by extension the world around me and how I look at it through a viewfinder.

A more startling realisation though, is that even when not doing this challenge I should be taking less photos, not more. This is going to be a hard balance to achieve, I think, since the proliferation of cheap storage and the desire not to miss a potentially good photo go really well together.

With this in mind, I went to a photo store in Stockholm yesterday and asked if they had any SD cards that were one gigabyte or smaller. I may as well have asked for a floppy disk.

You can follow along with my Time Warp challenge over in my Time Warp set on 500px.

2013 Mac Pro Review: Developer Edition

On June 10th 2013, I was sitting in the upper floor of Moscone West in San Francisco, along with a few thousand fellow iOS and Mac developers.

Roused by a somehow stirring video of nothing but words and dots, I sat back and let the RDF wash over me. iOS 7 is coming, and it’s going to be awesome!

The keynote started as normal. Retail stores. Shipped a quintillion iPhones. Yawn. Craig Federighi started demoing 10.9 Mavericks, which was pretty interesting, but the keynote was mainly for demoing consumer features. I noted a few things that looked particularly interesting and started getting excited about the new APIs that would be coming in Mac OS X.

Then, Phil Schiller came onstage. I’ve had a soft spot for Schiller ever since he pretended to be on a roller coaster while demoing iChat video effects during a keynote years ago, and I always mentally shout “PHIL!!!” when he comes onstage, but I don’t really know why. Phil started talking about minor bumps to Macbook Airs. Zzzzzz.

Wait, what’s this? Sneak peek of new Mac hardware? Finally, the new Mac Pro! Everyone cheers. I cheer. I feel a little bit sad that now the iMacs are powerful enough for everything I need I can’t justify the expense of a Mac Pro any more, but I’m excited. “We’re going to go a little over the top…” says Phil.

My ribcage starts to rumble. The bass ramping up throughout the room as the video starts was more of a feeling than a sound. Angles start flashing across the screen and not much else. Sigh. Another non-announcement.

Wait. It’s round. If it’s round it has to be…

Oh no. No no no no no.

It’s small.

“Can’t innovate any more, my ass!” Phil quipped, a smile on this face giving a way a sense of genuine pride. In seconds the non-annoucment had turned into a full-on discussion of the new machine.

Phil started talking about the design. He got to “Unified Thermal Core” and I was gone. They’ve made a spiritual successor to the G4 Cube! Phil started reeling off numbers I didn’t care about as I worked myself into a tizzy.

You see, I have a special bond with the G4 Cube. It was my first “real” Mac, and my mother bought it for me when I was a kid. I admired the beauty of the engineering of that machine. I learned to program on that machine. I cycled six miles to my local Mac reseller the day Mac OS X 10.0 came out and excitedly cycled home in the rain to install it on that machine. I’ve had many Macs since, but none had the soul and beauty of the G4 Cube. Coupled with a pile of nostalgic memories, I loved that machine so much I still have one to this day.

Generation Gap

Well, that was it. There is no way I couldn’t have one. I let my fiancée know the bad news, then tweeted what I thought was a fun but stupid screenshot of our conversation in what turned out to be my most popular tweet of all time.

Don’t worry — we still got married.

Now, time to retroactively justify spending so much money on a computer!

The Review

Disclaimer: This review is about day-to-day usage of a 2013 Mac Pro from the point of view of a Mac OS X and iOS developer, using tools like Xcode to get work done. The benchmarks here are only useful when compared to other benchmarks in this post, and my main focus is the overall “feel” of the machine compared to other modern Mac equipment rather than raw numbers. For detailed, millisecond-precise benchmarks I can highly recommend Ars Technica’s review of this machine.

What I’m Working With

Let’s begin by discussing what I’m working with. First, the hardware. I’ll mainly be comparing the Mac Pro with my work-provided 15” Retina MacBook Pro since they’re the two machines I have access to, and my wife won’t let me fill up her iMac with my crap (which, to be fair, is probably a wise choice).

2013 Mac Pro2013 15” Retina MacBook Pro
CPU3.5 GHz 6-Core Intel Xeon E52.7GHz 4-Core Intel Core i7 “Ivy Bridge”
RAM32GB 1867 MHz DDR3 ECC16GB 1600 MHz DDR3
GraphicsDual FirePro D700 6GBNVIDIA GeForce GT 650M 1GB
Storage1TB PCI-E SSD256GB PCI-E SSD
Specced Price$5,799 / £4,739 / €5,199 (DE/FR)$2,799 / £2,399 / €2,799 (DE/FR)

As for coding projects, I deal with a number of projects both at work and personally as side projects. Of these, I’ve chosen two of them to use in this review — for simplicity’s sake, I’ll call them Large Project and Small Project.

I’ve chosen these projects as I feel they reflect two common use cases — Large Project is a product typical of a cross-platform company with many people working on components of the same product, and Small Project is a typical small app that a small development shop or single developer might produce in a couple of months.

To reiterate my disclaimer above, I’m not going to go into detail about the exact number of lines of code, partly because of sensitivity concerns as I’m talking about a commercial application, and partly because it doesn’t really matter. However, to give you an idea of the size of the projects:

Small ProjectLarge Project
Derived Data*150MB3.98GB
Debug Binary Size**2MB105MB
No. of Source Files45 Obj-C .m, 30 C++ .cppI have no idea. A lot.
Benchmarked SDK & ArchitectureMac OS X 10.9 (x86_64)iOS 7.1 Simulator (i386)

* A project’s “Derived Data” is a collection of files generated by Xcode while indexing and building a project. It contains object files, indexes, build logs and various other files that allow Xcode to cache unchanged parts of a project for faster incremental building. The size was measured by deleting the project’s existing Derived Data folder, opening the project and doing a single debug build for a single architecture, then waiting for Xcode’s indexing process to complete.

** Debug binary only, no resources, for a single architecture.

The Small Project is a small Objective-C Mac app that contains 3 targets, all of which are dependencies of the main application and are built as part of a normal build. It contains some C++ code in the form of a third-party open source library, and has a nice and simple build process — open Xcode, push build.

The Large Project is a large iOS app that contains over 100 targets, most of which are dependencies of the main application and are built as part of a normal build. Some targets are heavily or completely C++ based, and the project has a very complex build process involving a wide variety of tools and build scripts in various languages.

Benchmarks

Alright, let’s get down to some benchmarking!

Build all the things! Activity Monitor wasn’t running during benchmark runs, but typical day-to-day apps were (email, Safari, Twitter, etc) to reflect a “normal” environment.

Since my Mac Pro has 32GB of RAM, I also benchmarked building the projects while using a RAM disk for Xcode’s Derived Data folder. I didn’t do this on the MacBook as 16GB isn’t enough to do this with the Large Project.

Sidebar: If you’re a developer reading this, I made a little command-line tool that simplifies the process of creating a RAM disk, designed to be friendly to being run at startup. You can find it over on my GitHub pages.

The builds had some Xcode debug build time optimisations applied as described over here, and are all debug builds for a single architecture.

Small ProjectLarge Project
MacBook Pro9 seconds6 minutes, 2 seconds
Mac Pro (SSD)6 seconds3 minutes, 58 seconds
Mac Pro (RAM Disk)5 seconds3 minutes, 40 seconds

As you can see, the Mac Pro builds projects around a third faster than my MacBook, which, in itself, isn’t all that surprising. With the Derived Data folder placed on a RAM disk, the Mac Pro is 40% faster than the MacBook.

One nice thing to note is that while doing these benchmarks, I had all six cores of the machine pegged at 100% for towards an hour. During that time, the fans of the Mac Pro barely made a whisper — a welcome change from the screaming fans of the MacBook.

A Note On Release Builds

I considered doing benchmarks using release builds as they’ll be slower as optimisation is CPU-intensive, and if you’re building for multiple architectures build time will almost increase linearly (around twice as long for two architectures, three times as long for three, etc). As a rough guide, a typical release build for an iOS app that supports both arm64 (iPhone 5S and iPad Air) and armv7 (everything else at the time of writing) will take roughly 2.5x as long as a single-architecture debug build.

However, this review is focusing on a developer’s day-to-day workflow rather than build server duties. However, I did do a couple of release builds of the Large Project, and you can expect speedup to be similar to that of debug builds.

Day-To-Day Workflow in Xcode

This is where things get interesting. Clean builds only tell a small portion of the story — day-to-day, clean builds are somewhat of a rarity. Instead, we make many small incremental builds as we write some code, make sure it builds, then test the changes out by running the application or running unit tests.

My MacBook is my daily work machine, and we’ve been at each other’s side for a year or so now. I know it in and out, and until I started working on the Large Project with my Mac Pro, it felt fine.

A typical small task I might do involves finding a file, opening it up and finding the method I need to work on. Then, I’ll need to quickly look up the API reference of something I need to add to that method, then write the new code and test it.

It goes like this:

  • Command-Shift-O to open “Open Quickly”.
  • Start typing the class name HTTPIma….
  • When the file comes up in the list, press Return to open it.
  • Navigate to the method I need.
  • Declare an instance of the new API I need to use: NSURLConnection *connection;.
  • Command-Option-Click the NSURLConnection name to open its header in the Assistant Editor.
  • Read the documentation and amend my code accordingly.
  • Close the Assistant Editor.
  • Run the application and test the new code.

Xcode’s “Open Quickly” panel

After a week using the Mac Pro and doing this regularly, I tried it again on my MacBook.

  • Command-Shift-O to open “Open Quickly”.
  • Start typing the class name HTTPIma….
  • When the file comes up in the list, press Return to open it.
  • Open Quickly is still processing my typing, so by the time the Return registers, a different file is selected.
  • Open the wrong file. Grumble.
  • Repeat, this time waiting until Open Quickly has settled down.
  • Navigate to the method I need.
  • Declare an instance of the new API I need to use: NSURLConnection *connection;.
  • Command-Option-Click the NSURLConnection name to open its header in the Assistant Editor.
  • Beachball.
  • 5 seconds later, the Assistant Editor appears.
  • Read the documentation and amend my code accordingly.
  • Close the Assistant Editor.
  • Beachball.
  • 5 seconds later, the Assistant Editor disappears.
  • Run the application and test the new code.

My MacBook can’t possibly be this bad, can it? After working on the MacBook for a few hours, I got used to it again and realised that it didn’t seem slow before because I’d briefly do something else while waiting for Xcode to catch up — glance at Twitter, take a sip of my drink, etc.

My whole Xcode experience is like this on my MacBook with the Large Project. Getting to the Build Settings pane from a source editor takes a good few seconds as it takes time to bring up each new panel as you navigate there. After a year of nothing else I’d gotten so used to it I didn’t even notice it any more.

I’ve found this week with my Mac Pro to be far more productive than working with my MacBook. It may partly be due to the fact I’m also working from home, away from the distractions and annoyances of the office, but the fact I don’t have time to glance at Twitter or sip my drink as I navigate around certainly helps keep my concentration sharp.

It’s important to note that only the Large Project makes my MacBook behave this way. Working on smaller projects, including work projects much larger than the Small Project I talk about here, the Xcode experience is as fast and painless as it is on the Mac Pro.

Day-To-Day Workflow in Other Development Tasks

I don’t really use huge datasets in anything other than Xcode, so nothing surprising here. grep is noticeably faster on the awesome SSD, as is switching around branches.

One thing that is nice is the ability to run several virtual machines at once without having to care about system resources. This is particularly handy when testing features that involve remote controlling — I can have multiple iOS Simulators running at once without problems.

Conclusion

If you have a reasonably modern, well-specced machine and are bumping into its limits, the Mac Pro gives a surprising amount of extra freedom that I didn’t expect. My MacBook isn’t a bad machine at all, and I just assumed the large project I work with would bring Xcode to its knees on anything. I’ve felt a genuinely large improvement to my day-to-day productivity on the Mac Pro, to the point where working on my MacBook feels clunky and annoying.

If your current workload is bringing your computer to a grinding halt, you might find the Mac Pro gives a refreshing freedom to your day-to-day workflow. If that’s the case, I’d really recommend it.

Otherwise, I’d really struggle to justify the cost purely on a development basis and have a really hard time imagining an indie developer or small development shop generating a project large enough to see the benefits — especially since the newer iMacs and MacBook Pros are excellent development machines and give a great price-performance ratio.

In short, if you have a modern machine and aren’t already thinking “I really need something more powerful than this”, the Mac Pro is really hard to justify.

Unless you loved the G4 Cube — then you should buy one anyway, because the G4 Cube was awesome.

Perhaps I should get that printed on a mug.

Xcode Bots: Common Problems and Workarounds

I love Continuous Integration. I’ve never bothered with it for my side projects since it didn’t seem worth the effort, but now it’s built into Xcode I thought “This is Apple! Two clicks and I’m done, right?”

Wrong.

I spent a few hours battling with the Xcode service on my Mac OS X Server, and below I detail workarounds for the problems I encountered getting my projects to build.

Don’t get me wrong — I love Bots, and I’m sure that in a few months these issues will be fixed as they’re getting tripped up by very common situations. Remember to file those Radars!

After a little work, my main side projects are continuously integrated. Hopefully this post will help you do it faster than I did!

Note: All these tips assume you’re using git. If not, well, you’re on your own!

Repositories that require an SSH key to access

If you host your repository on GitHub or similar, you’re likely to use an SSH key to access your code — especially if you have a private repository. GitHub will actually let you check out over https, but where’s the fun in that when you can follow this simple seven-step process?

1) If your SSH key requires a password, you need to make a password-less version of it using ssh-keygen -p (when it asks for a new password, just hit return).

2) In Xcode, choose Product → Create Bot…, name your Bot and untick Integrate Immediately and click Next.

3) In the next pane, choose Guest as your authentication method. This will succeed on your local machine since you have your keys installed. Continue through and create your Bot.

4) Open the Server application and find your new repository and edit it. Change the authentication method to SSH Key and the user name to the correct value (this is git for GitHub).

5) At this point, you’ll notice a new SSH key has been created for you. Either upload the public key to your host, or click Edit… to replace it with your own password-less keys.

6) Save your changes, return to Xcode and click “Integrate Now” in your new Bot.

7) Success!

8) File a Radar asking Apple to add the SSH Key authentication option to Xcode itself. Feel free to duplicate mine: #15184645: Bots: Can’t enter an SSH Key to authenticate a repository in Xcode.

It’s worth noting that you can make this process slightly simpler by manually adding the repository to the Xcode service in the Server app before creating your Bot. This way, you can set up your SSH keys right away. Make sure, however, that you get the URL of the repository to match the URL you have checked out on your local machine, otherwise Xcode won’t pick it up.

Code Signing and Provisioning Profiles

Ahh, Code Signing. It just got easy, and now there’s a new spanner in the works.

Bots run their builds in their own user. This means that even if they build fine when you log into your server, they may fail to build in the Bot. The tricky part here is making your private keys available to the Bot — downloading the certificate from Apple’s Developer Center doesn’t give you back the private key, so you’ll need to import it from a machine with a working build setup using Xcode’s Developer Profile Export/Import.

Once you can log into your server and build your project with signing, a simple way to allow Bots to access your signing certificates is to copy them from your user’s Keychain to the system Keychain.

Make sure you copy the private key by dragging it to the System Keychain. The certificate will be copied as well.

Once you have your certificates in the System Keychain, your Bots will be able to access them.

Adding your Developer Team to the Xcode service in the Server app should automatically deal with Provisioning Profiles. If not, put your profiles in /Library/Server/Xcode/Data/ProvisioningProfiles.

Submodules

Submodules work fine as long as you don’t have a detached HEAD. If they require SSH keys to access them, after creating your Bot you’ll need to go over to the Server app and manually set up the keys for each submodule.

If you’re not sure if you have a detached HEAD, you can check from the command line or Xcode. On the command line, run git status in each submodule. If you get something like # HEAD detached at 89970d0, you’re in a detached HEAD. From Xcode, try to create a Bot. When it’s time to define authentication for each repository, Xcode will tell you the state of each submodule.

If any of your submodules read “(detached from…)”, they won’t work.

To fix this, you need to get back on a branch. The quickest, hackiest way to do this is to run git checkout -b my-awesome-branch in the submodule to make a new branch. Once that new branch is pushed and available on your remote, your Bot will be able to check it out correctly.

Note: I’ve noticed that Xcode 5.0.1 doesn’t seem to refresh branch information quickly. Try relaunching Xcode if things get weird.

Don’t forget to file a Radar! Again, feel free to duplicate mine: #15184702: Bots: Checkout failure in git projects that have submodules with a detached HEAD.

I made a mistake and now I can’t fix the repository from Xcode!

If you accidentally added a Bot with incorrect authentication details or some other mistake, deleting the Bot may not fix it. You also need to delete the repositories from the Xcode service in the Server app. When you do that, Xcode will offer to reconfigure them when you create a new Bot.

The Future

Despite these niggles, I really love Continuous Integration and Bots. I’ll try to update this post as I find more issues, and hopefully as the Bots feature matures and these issues go away. Follow me on Twitter to get update announcements.

Updates

  • October 9th, 2013: Added Radar bug numbers for SSH keys and submodules.