CoPilot – Xcode collaborative editing

Here’s something I’ve been working on recently. It’s not quite done yet but it’s very close. The following video should tell the story best:

CoPilot is an Xcode plugin written in Swift. I will publish it as open source very soon.

“Try” – wrestling with NSErrorPointer

A great aspect of Swift is its interoperation with Objective-C frameworks, allowing us to use existing APIs and code. While that works very well technically, Swift does promote a different style and this leads to some situations where the new is slightly at odds with the existing.

One such area is calls to methods with an NSErrorPointer parameter (i.e. NSError ** parameters in Objective-C):

let fm = NSFileManager.defaultManager()
var error: NSError?
let files = fm.contentsOfDirectoryAtPath(".", error: &error)

That doesn’t look very swift-y. Even in Objective-C the NSError ** parameter is kind of ‘ugly’ in the sense that it’s rather uncommon to pass parameters by reference and have them modified inside methods or functions.

In order to improve on this I’ve started using the following try function:

if let error = try({ error in
    let fm = NSFileManager.defaultManager()
    let files = fm.contentsOfDirectoryAtPath(".", error: error)
}) {
    println("failed with error: (error)")
}

The implementation of try is rather simple:

func try(block: NSErrorPointer -> Void) -> NSError? {
    var error: NSError?
    block(&error)
    return error
}

Note that we now pass in error instead of &error to contentsOfDirectoryAtPath(...) call. Wrapping it like this gives us the opportunity to hide the fact that we’re dealing with an NSErrorPointer behind the scenes.

This sort of resembles the typical try-catch pattern, at least conceptually:

try {
    // success path
} except(error) {
    // error path with an error variable
}

But while this looks similar to a try-catch, it’s undeniable that the if let error = ... puts the error handling frontmost. The construct is dealing with the error handling mainly and the ‘happy path’ is pushed to the background.

Another approach, therefore, especially if the method being ‘tried’ has a return value, is to wrap this value and a potential error in a Result<T>:

switch try({ error in
    NSFileManager.defaultManager().contentsOfDirectoryAtPath(".", error: error)
}) {
case .Success(let value):
    println(value.unbox)
case .Failure(let error):
    println(error)
}

Result<T> is an enum tying result and error states together. I've first come across this in the great book Functional Programming in Swift by the objc.io folks.

While it’s a bit more verbose and doesn’t resemble a try-catch, it’s now very clear what the results are. It’s unfortunate that we need to unbox the value but that’s unavoidable, for now at least.

The whole definition of ‘try‘ is available in this gist, including Result<T>.

Swift & Nimble Testing

I’m a big fan of unit testing and when I discovered the BDD framework Nimble with Swift support a couple of months ago I was delighted. One of its advantages is that instead of using the XCTAssertEqual macros you can write:

expect(answer).to.equal(42)

which, thanks to Swift’s support for operator overloading, can be made even more expressive:

expect(answer) == 42

However, there was one area where I found the syntax a bit verbose – when comparing floating point numbers:

expect(answer).to(beCloseTo(42.0))

What this does is simply make the comparison fuzzy by allowing a difference of 0.0001 – the default delta as defined by the framework.

It would be great if we could write it this way:

expect(answer) ≈ 42.0    // type Option-x for ≈ (U.S. keyboard)

And actually we can, thanks to Swift’s support for custom operators. Now understandably people worry about misuse of custom operators and I fully agree that you need to be very careful when and where you use them. But I feel that test code is a good place where a readability ‘optimisation’ like this one can be applied.

All unit tests do is compare expectations to actuals and anything we can do to make this concise and readable for the actual values stand out over the boilerplate is a win. Custom operators are a good tool for this, especially if they mirror universal symbols like the mathematical sign of inequality.

Justifications aside, how does this work?

Nimble allows you to define so-called custom matchers that extend the set of validations:

public func equal(expectedValue: T?) -> MatcherFunc {
  return MatcherFunc { actualExpression, failureMessage in
    failureMessage.postfixMessage = "equal <(expectedValue)>"
    return actualExpression.evaluate() == expectedValue
  }
}

The existing package already provides a beCloseTo matcher for decimal number comparisons and it is then straightforward to define an operator for it:

infix operator ≈ {}
public func ≈(lhs: Expectation, rhs: Double) {
    lhs.to(beCloseTo(rhs))
}

But what’s missing here is the case where you specify a delta different from the default:

expect(answer).to(beCloseTo(42.0, within: 1.0)

In other words if we want to specify the delta (and that’s probably quite common) we’re back to the more verbose version. Ideally we’d like to write this as:

expect(answer) == 42.0 ± 1.0    // type Option-Shift-= for ± (U.S. keyboard)

Turns out we can, and the way this works is as follows. First we create a binary operator ± that converts the value to its left and the delta to its right into a tuple (expected: Double, delta: Double):

infix operator ± { precedence 170 }
public func ±(lhs: Double, rhs: Double) -> (expected: Double, delta: Double) {
    return (expected: lhs, delta: rhs)
}

Then we add an overloaded method which takes an Expectation<Double> and the tuple (expected: Double, delta: Double) as parameters:

public func ≈(lhs: Expectation, rhs: (expected: Double, delta: Double)) {
    lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
}

These changes have been kindly accepted and integrated by the Nimble team into the framework as of Jan 5 (commit e7bafdb)

Part of this change was also an extension for comparisons of arrays of numbers:

expect([0.0, 1.1, 2.2]) ≈ [0.0001, 1.1001, 2.2001]
expect([0.0, 1.1, 2.2]).to(beCloseTo([0.1, 1.2, 2.3], within: 0.1))

See the Nimble documentation for further examples.

SpriteKit and Swift – converting a project from Objective-C

In November 2013 I was looking for an excuse to play with SpriteKit and came up with the idea for a mini-game with a Christmas theme. This is what it looks like:

When Swift was announced at WWDC 2014 I was eager to give it a try and looked through my grab bag of side projects for a suitable candidate to try migrating a project from Objective-C to Swift. ’Shooter‘ was a good candidate, because it‘s not too big but also not too trivial.

The source code is available on github and I‘m planning to post about lessons learned when transitioning from Objective-C to Swift in a future update. However, the commit history already tells a pretty decent story of the transition. Commit ab6df2e merges the ‘swift’ branch into master and 8ed7782 is where the journey starts.

Switfly build a Mac app

Curious about Swift, I went ahead and translated Matt Gallagher‘s example from Objective-C to Swift. If you stick this in a Playground file it will launch a minimal Mac app. Or you create a simple text file and chmod +x it for direct execution from the command line.

#! /usr/bin/swift -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk

import Cocoa

var app = NSApplication.sharedApplication()
app.setActivationPolicy(.Regular)

var menuBar = NSMenu()
var appMenuItem = NSMenuItem()
menuBar.addItem(appMenuItem)
app.mainMenu = menuBar

var appMenu = NSMenu()
var appName = NSProcessInfo.processInfo().processName
var quitTitle = "Quit \(appName)"
var quitMenuItem = NSMenuItem(
    title: quitTitle,
    action: Selector("terminate:"),
    keyEquivalent: "q"
)
appMenu.addItem(quitMenuItem)

appMenuItem.submenu = appMenu

var window = NSWindow(
    contentRect: CGRect(x: 0, y: 0, width: 200, height: 200),
    styleMask: NSTitledWindowMask,
    backing: NSBackingStoreType.Buffered,
    defer: false
)

window.cascadeTopLeftFromPoint(NSPoint(x: 20, y: 20))
window.title = appName
window.makeKeyAndOrderFront(nil)
app.activateIgnoringOtherApps(true)
app.run()