Why We Must Fight

We have a pernicious, systemic problem in what we call the software industry. We are excluding people from participating on the basis of superficial differences, especially gender. The wages of this collective stupidity are reduced productivity (and therefore prosperity) and a more impoverished community lacking in the kind of diversity that makes human societies worth living in.

There are many important reasons why we must create this improved environment, but two that especially stand out to me. Firstly, there's the moral imperative; that equal consideration of interests is just the right thing to do. We don't really need any more justification than this to proceed confidently towards the goal of creating a better community.

First, let's remove a common source of confusion. By equality I mean 'equal consideration of interests'. I don't mean pretending that everyone is the same; that's clearly untrue. For instance, we happily provide urinals for biological males because of anatomical differences in the sexes, but that doesn't mean we're neglecting the interests of biological women in doing so. To argue otherwise is to confuse similarity and equality (in much the same way that an uneducated person may confuse the uncertainty principle with the observer effect).

So that form of opposition to equality is nonsense, and to use it is to invite embarrassing rhetorical defeat. In the case of women in tech, equal consideration of interests means that today we have to actively do more for women because the environment is currently hostile to them at every level. Doing nothing does not create a 'level playing field', a meritocracy, or any other wonderful ideal. We need to actively push back and explicitly create diversity in tech. If you think that itself is injustice, think harder.

There's another, nastier variation on this argument, insinuating that action is only necessary because women lack the innate ability to succeed alone. It's an argument that's so weak it is hardly ever directly vocalised, but it's a widely held belief made apparent by complaints about the danger of using 'positive discrimination' or 'affirmative action' to bring about change. I almost feel pity for those trying to use this argument, but that won't prevent me from destroying it.

Men and women are not opposites. If men are from Mars, women are from Mars. Even if you compare them as two populations and not individually, the physical differences are small and the intellectual differences don't exist. The reason for this is that in reality, human sexual dimorphism just isn't that large. Go look it up. Then deal with it.

Note that even if this was not the case, it still wouldn't be an argument against the equal consideration of interests. I'm just refuting it because it's a fallacy; if you follow this line of reasoning you can't win, you can only lose.

So if you're truly looking for the reasons why there is a huge gender imbalance in software development, look elsewhere. A woman programmer is just as able as a man. Differences in capability cannot explain why by some estimates only 1.5% of contributors to open source software are women. An openly hostile environment that nobody should be subjected to can explain that difference.

So having put the fallacies opposing equality to bed, let's consider the second pillar of our reasoning; the economic argument. As an industry (and as a society) we're far poorer when we arbitrarily exclude people from contributing. We are living in a period where demand for experts in the field of high technology far outstrips supply and will continue to do so for the foreseeable future. This means that there's no need for intelligent developers to fear new entrants to the market, in fact our future depends on them. Sadly, many who could contribute their talents to our collective endeavour are unable to do so, because they're missing from our community. They either left, never felt able to join, or contribute less as a result of the environment we've created.

Well, it's time to uncreate that environment. To reform the landscape is going to take time. It will also require us to use every weapon in our arsenal, from the precise to the blunt, the inoffensive to the blasphemous. That will include things like 50% women speaker lineups at conferences, a zero tolerance to bullying & harassment with strong legal enforcement, and high profile all-women teams to counter the current men-only cabals. Not all of the weapons will be compatible with our current illegitimate notion of meritocracy. That is by design.

If you believe in real equality, then it's time for us to fight. It's a war that we can win.

Code Injection With Xcode 4

A long time ago, Xcode 3 came with the ability to 'Fix and Continue', effectively allowing you to inject code into a running App without another build and run cycle. This is a powerful tool because it can shorten your debug cycles and thus increase your productivity. This is especially true for projects with longer than average build times; if your mind is in full flow and some process takes more than a few seconds, it's easy to become distracted and start doing something else (such as writing blog posts).

Xcode 4

John Holdsworth has written an excellent plugin for Xcode 4 called Injection for Xcode. There a video on Vimeo showing the tool in action, demonstrating changing code in the iOSGLEssentials project and then viewing the results. After first installing the Xcode plugin and then patching your project, making a change is as simple as selecting 'Inject Source' (or hitting cmd-=).

This approach is not just for the simulator; it works on devices as well, with the caveat that you'll need to add the following post-build script action for code signing to work:

echo "$CODESIGNING_FOLDER_PATH" >/tmp/"$USER.ident" && echo "$CODE_SIGN_IDENTITY" >>/tmp/"$USER.ident" && exit;

How does it work? The plugin takes advantage of Cocoa's bundles pattern and the Objective-C runtime's ability to modify mappings from a selector to an implementation. When you patch your project, you're actually importing BundleInjection.h via Prefix.pch (Injection for Xcode only works when INJECTION_ENABLED is defined, so it won't interfere with a released App).

When you 'Inject Source', that gets imported into a bundle project which is built and loaded by your App. When that bundle loads, its +load method calls [BundleInjection loadedClass:theNewClass notify:flags], swizzling the new implementations onto the original class.

Final Thoughts

Code injection is a great tool to have, but be careful that it doesn't become a crux for trial and error development. The shorter cycles can tempt you into just experimenting without first having a solid mental model of what should be happening after you hit cmd-r. If used wisely, code injection lets you spend more time developing and less time watching Xcode build your App.

Better Framework Versioning

TL;DR version: arbitrary version numbering makes life hard for developers using your framework. Adopting better versioning practices will save your users thousands of development hours in wasted effort and frustration. 

The Challenge

How do you version frameworks you build for other developers? The default approach is to just do what feels right, perhaps incrementing the version numbers using an automated build system, or simply numbering them sequentially whenever we release them. These instincts come from versioning for end users, who consume your software in a very different way. The issue with this approach arises when developers who use your framework have to manage multiple dependencies, decide when to update your framework, and keep legacy code updated. It's especially bad when multiple frameworks rely upon yours; without a precise idea of what each version number means there's no way of determining which is appropriate common version to use.

Semantic Versioning

Semantic versioning is one solution to this problem, and describes itself as "a simple set of rules and requirements that dictate how version numbers are assigned and incremented". The core principles are: 

  • A version number is made up of a major, minor, and patch version (e.g. 1.0.1)
  • Version 0.0.0 is for initial development; 1.0.0 defines the stable, public API
  • Each change to that API gets a new version number (no re-releases)
  • Major version increments when backwards-incompatible updated are made
  • Minor version increments when new, backwards-compatible changes occur
  • Patch version increments when backwards-compatible internal bug fixes occur

The upshot of this approach is that for the cost of some discipline when setting version numbers, you'll experience much more satisfaction from the users of your libraries. The reason for this that better consistency and precision eliminates ambiguity, which has the potential to break your user's code and generally make like difficult. The drawback is that you'll probably have to version your framework manually. There is scope to automate the versioning process, but it would require having an extremely tight set of integration/acceptance tests to exercise your public interface, and something to check out and run previous versions of the tests.

Framework Versioning

The awesome CocoaPods Objective-C library manager uses semantic versioning by having library authors tag the git repo of the library with the semantic version number. It's a good idea to get into the habit of tagging versions in this way, even if you're not using CocoaPods yet.

Apple provides its own guidance about framework versioning here (UPDATE: These docs are horribly out of date, see the note below), including an expanded definition of when exactly to increment a major version number. You have to be careful here, because their guidelines are similar but differ from Semantic Versioning. What both agree on is the emphasis on infrequent major releases and not removing interfaces simply because you don't like them anymore. If I could summarise this approach to framework versioning, it would be no sudden moves, e.g:

Don’t delete interfaces. If a method or function no longer has any useful work to perform, leave it in for compatibility purposes.

It feels good to remove code, but we should fight that instinct when it comes to old interfaces. They should effectively be considered cast-iron treaties that can only be revoked after lengthy debate and clear signposting.

Deprecation

If we do anticipate removing methods or properties, we should warn the framework user in advance. We can do this by signposting upcoming changes to major revisions in minor releases. Use the DEPRECATED_ATTRIBUTE macro to warn a framework user that a method or property is going away: 

- (void)aMethod DEPRECATED_ATTRIBUTE;

DEPRECATED_ATTRIBUTE is a macro that expands to __attribute__((deprecated)). You can use either depending on your coding style guide.

Xcode

UPDATE: The following instructions are based on out of date documentation, as pointed out to me by Justin Spahr-Summers (@jspahrsummers). In fact, you should just currently ignore the Xcode versioning altogether.

Whether you're building a 'fake' iOS framework or a real one, the frameworks you builds can contain all the major versions you release. This is possible because a framework is a bundle with a directory structure like AmazeKit.framework/Versions/A/AmazeKit where A is the major version.

There are 3 build settings you can use to set framework version within Xcode 4. They are 'Framework Version', 'Current Library Version', and 'Compatibility Version'. These don't map exactly to the concepts in Semantic Versioning. The current library version is meant to be used internally to track development, while the compatibility version signifies any changes (including addition) to the public interface. You can almost think of the current library version as the patch number and the compatibility version as the minor version, but they're meant to advance together when the compatibility version is updated. The framework version is equivalent to the major version in Semantic Versioning.

Conclusion

So, how should you approach framework versioning? If you're distributing something with CocoaPods, you can just follow their guidelines; CocoaPods uses Semantic Versioning. If you're distributing something in a more traditional way you should follow the Semantic Versioning guidelines and leave the Xcode build settings at the defaults:

  • In your version control system, tag your releases with the semantic version number.
  • Keep the framework version number at the default, producing one compiled version per framework.
  • Think about providing runtime access to the full current version number.

It would be great to hear some feedback on how you handle Xcode framework versioning. I'll be gathering those opinions and updating this post at a later date if there are any superior approaches.

Creating Documentation Sets With Appledoc

Creating documentation sets to distribute with your frameworks is easy using the open source appledoc project from Gentle Bytes. Xcode documentation typically takes the form of class documentation and companion guides, distributed in a docset bundle (a folder hierachy containing human and machine readable documentation files). Class documentation is generated from your source code and properly formatted comments, while the companion guides are handwritten and formatted from markdown.Relying on specially formatted comments to populate the class documentation means that you have control over what level of detail is included in the documentation. The most important points to be aware of are that multi-line comments must start with a double asterisk (/**) and single line comments start with a triple slash (///) to be included in the docset.

/** Included */ vs. /* Excluded */ 

/// Incuded vs. // Excluded

Once installed, appledoc itself is run from the command line, using the --output flag to specify your completed docset's path, e.g:

appledoc --output "${SRCROOT}/../Docs"

There are flags to control the properties of the resulting docset, e.g:

  • --docset-bundle-id <string>         [*] DocSet bundle identifier
  • --docset-bundle-name <string>       [*] DocSet bundle name
  • --docset-publisher-id <string>      [*] DocSet publisher identifier
  • --docset-publisher-name <string>    [*] DocSet publisher name

There will be classes in your project (third party or internal code) that you don't want to be visible in the documentation set, which you can exclude using the --ignore flag, e.g:

--ignore XYZMyInternalClass.m

To include the companion documentation, use the --include flag to point to a folder where you'll create the companion guides, e.g:

--include "${SRCROOT}/../Docs/Companion"

The guides themselves are plain text or html files with the suffix -template, e.g. mycompanionguide-template.markdown. These follow the same formatting rules as source code comments.

Further Reading

More background on documentation sets can be found at this Apple developer page.

Who Watches The Watchdog

Remember that when you debug your App with Xcode, the watchdog is disabled in order to compensate for time spent attaching the debugger. That means you can't test how the watchdog will judge your App's launch performance; poor performance will get your App killed and rejected from the App Store.

Knowing this makes reproducing launch-related crashes from your QA team or beta testers a far easier task, however you should really strive to write performant code first time. These teaching resources from Apple will help you to avoid getting 0x8BADF00D:

Your Classes Should Use Three Letter Prefixes

Objective-C class names must be unique, not only in your own code but in any frameworks you depend upon. The Objective-C convention is to prefix every class, thus creating a kind of namespace for your application or framework. It's easy to forget that you're advised to make them at least 3 characters in length, as mentioned on the Apple developer site:

In order to keep class names unique, the convention is to use prefixes on all classes. You’ll have noticed that Cocoa and Cocoa Touch class names typically start either with NS or UI. Two-letter prefixes like these are reserved by Apple for use in framework classes. Your own classes should use three letter prefixes.

You might think that's overkill, but really it's a prudent guideline when we're all living in this one giant namespace. This is a case of do I say and not as I do, and though there are plenty of frameworks that ignore this advice, it is good practice.

Improving Updates With OS X Server

When you're doing iOS and Mac development, you have to manage a lot of updates, especially if you're running your own test lab.

OS X Server 2.2 introduced a caching service which takes away some of the pain of keeping all your devices and Apps up to date. Effectively, when one of the devices on your network requests a binary from the App Store, the caching service saves that binary and serves it up again in response to future requests. This has the effect of dramatically increasing the speed of subsequent updates, and reducing the use of your Internet bandwidth. It's especially useful when bandwidth is constrained; think of it as DRY but for your Internet connection and not your code.

Installation

OS X Server is now delivered as an App that can be easily installed on top of your existing OS X install via the Mac App Store. Properly configuring the server component and getting the most from all the services on offer is beyond the scope of this post; today we're just focusing on the utility of the caching service. There's more information on setting up a server available here; it's so difficult that there actually is a step three.

Configuration

The server App has a pane that allows you to adjust some basic properties of your cache, such as cache size and destination volume:

Caching Service Options

Advanced configuration is achieved by editing the Config.plist for the service directly. This is located here: /Library/Server/Caching/Config/Config.plist. See this knowledge base page for more details of the options available. Most of the useful advanced options relate to scaling the caching network above one server, something you won't have to worry about at a small scale.

Once you've configured your caching service, anything you download via the App Store via your local network (including software updates & Xcode) will be cached to your server.

A Word About iOS Updates

Since the introduction of the iPhone 3GS, Apple effectively prevents you from downgrading to a previous major revision of the OS, even if you have a cached copy. It's worth bearing this in mind if you intend to support an older version in the future - unless you cache a copy of the SHSH blob for that individual device, you will not be able to downgrade the OS in future, even using jailbreaking tools. In that case it may be worth acquiring a new device instead of updating.

Alex Breakpoints In Networking Code

Not That Alex.

You're probably aware that you can now avoid littering your code with NSLog statements and instead use Xcode's breakpoints with a log message attached. This has several advantages over NSLog; your code is kept cleaner, you can share logging with others via shared breakpoints, and you have far more control over what's happening.

Audio Actions With Breakpoints

Breakpoints in Xcode 4 can go further though. Say you want to test some networking code that runs at regular intervals in background mode. You don't really fancy spending 30 minutes of your day staring at the console waiting for log statements to appear, so instead you set a breakpoint in your code with a sound action associated with it:

Setting An Audio Breakpoint

Now, a sound effect will be played every time the breakpoint is hit. This is very useful when you're testing an App but only want to be notified when something interesting happens. An audio breakpoint frees you to work on something else at the same time as doing some long-running testing.

Alex Breakpoints

We can take this approach further with speech breakpoints. These allow you to get logging information audibly, courtesy of Mac OS X Speech (the default text-to-speech voice is called Alex, which is where the title of this post comes from).

Setting A Breakpoint To Speak

This is enormously powerful as it means Xcode can read aloud anything you want to know about program state. In our networking code example, you could keep track of the number of times the code was executed, how long your App has been backgrounded, how many bytes have been downloaded etc. giving you an insight into what is happening at any given moment. One thing to note is that it's a very different modality than the console, one that benefits from terse rather than verbose statements. Experiment to find the best approach in your case.

ARMv6 Support With Xcode 4.5

TL;DR Version:

You can create an iOS binary that spans armv6 - armv7s with Xcode 4.5, but it takes some extra work.

The Problem

Xcode 4.5 has dropped support for armv6, making a quick and easy build for all iOS devices more difficult.

First off, let's be clear: I do not endorse building for armv6 at this stage in the iOS lifecycle! In most situations your wisest course is to drop support for armv6 and proceed with your project under Xcode 4.5's default settings. For the purposes of this post, I'm assuming you're aware of that and you find yourself in the position where you need to get around this limitation.

Two Solutions

Prerequisites

You need to have multiple versions of Xcode installed for the following solutions to work.

Option One: Just Use Xcode 4.4.1

The first solution is to just build with a previous version of Xcode - 4.4.1 for example. Your armv6-armv7 code will work fine on iPhone 5. Yes, that's right - you don't need an armv7s architecture to make iPhone 5 compatible binaries. There's a caveat though, which is why it comes with a bold warning to catch your eye when skimming this post. WARNING: This solution may work for internal enterprise projects, but it won't work when going to the App Store because Apple will no longer approve Apps built with Xcode 4.4.1.

Option Two: Multiple Builds & Lipo

The better solution is to have Xcode 4.4.1 build the armv6 binary, have Xcode 4.5.x build the armv7-armv7s/i386 version, and then combine them into a fat binary using lipo. The steps to achieving this are as follows: 

  1. Compile with Xcode 4.4.1 and copy the binary.
  2. Switch to Xcode 4.5.3 and compile there.
  3. Remove the i386 and armv7 architectures from the binary you created in step 1.
  4. Combine the two binaries into one fat binary.

It's probably best to do the process manually the first couple of times, then look at scripting the process to save time in future. Use xcode-select to specify which version you're building with in steps 1 & 2, and lipo for the binary manipulation in steps 3 & 4.

Overview Of Creating An armv6-armv7s Binary With Xcode.

Steps 1 & 2 assume that your 'Valid Architectures' and 'Architectures' build settings are $(ARCHS_STANDARD_32_BIT). This resolves to armv6, armv7, i386 on Xcode 4.4.1, but armv7, armv7s, i386 on Xcode 4.5.3. This creates a problem because we can't combine two binaries if they have overlapping architectures (in this case, they both contain armv7 and i386). 

Step 3 does just that. You can remove the armv7 and i386 architectures using lipo, e.g:

lipo myBinary -extract armv6 -output myArmv6Binary

...then, in step 4, combine the binaries from step 1 and 2:

lipo -create myArmv6Binary myBinaryFromStep2 -output myFatBinary

If you see an error like "myBinary and mySecondBinary have the same architectures (cputype (12) and cpusubtype (11)) and can't be in the same fat output file", check you're using the output from step 3. Your binary from step 3 should only have armv6 in it - check with the file command or lipo -info.

Quickly Reveal Binary Architectures Without Lipo

You can determine what architectures your binary contains without lipo using the 'file myBinaryName' command from the terminal:

Output from the file command, showing binary architectures.

In the example above, we see two architectures listed: armv7 and armv7s (listed as the rather less pithy cputype (12) cpusubtype (11)). 

Further Reading

This post from Matt Galloway on 'faking' an armv7 library is a good read. Although you'll likely never need to use the technique he describes, it gives you more of an insight into the relationship between armv7 & armv7s. 

Running Multiple Versions Of Xcode

Xcode.jpg

Working with multiple versions of Xcode was made easier with the switch to distribution via the Mac App Store. In the bad old days (i.e. before 2012), running multiple versions of Xcode was less straightforward.

Now, thanks to the .app Bundle structure, backing up your current version is as easy as copying the Xcode directory itself. Provided you rename the bundles, multiple versions can coexist together in your Applications folder. You can also exploit this to test out preview versions of Xcode from the Apple developer website. 

Using Virtual Machines To Manage Dependencies

Ever since Lion, Max OS X has been licensed to run as a guest OS in a virtual machine. This is enormously useful, as it gives us a way to effectively archive a particular combination of OS X, Xcode, and other Supporting tools without having to compromise our current setup. Modern machines have enough CPU and memory to this setup workable. I use VMWare Fusion, but something like Parallels will work equally well for this purpose.

When you work from a VM, revisiting an archived project from the past becomes infinitely easier because you have a development environment in stasis to accompany it. This is especially true if you've spent time on custom configurations, or if new versions of dependencies have broken key functionality in your project.

When working with other developers, this approach also has the benefit of removing another area of uncertainty when hunting down hard to fix bugs. Having a portable environment is incredibly powerful. It also allows you to quickly switch to a different OS/Xcode combination, which sometimes has advantages; in the image below we see a configuration of Lion and Xcode 4.5.2 (running on VMWare) that reinstates access to the iPhone 4 simulator.

iPhone Simulator 4.3 Available In Xcode 4.5.2 Running On OS X Lion In A VMWare Fusion Virtual Machine. This Simulator Is Not Available On Xcode 4.5.x Running On Mountain Lion.