Donald Hays

Enum Errors in Swift

December 16, 2014

Brad Larson of Sunset Lake Software wrote a blog post called “Why we’re rewriting out robotics software in Swift.” The article is a worthwhile read, and explains that an audit of their Objective-C code history revealed that approximately 40% of the shipped bugs would have been prevented by using Swift. Which is really interesting.

I would like to focus on a different part of the article, however. It’s actually more of an aside in the article itself, but it was something that really stood out to me: the use of enums instead of NSError in Swift. Brad goes into more detail on the subject in a mailing list post. After having played with it myself, I think using enums for errors is a really big win, and I intend to use the pattern in my own Swift code going forward.

What’s Painful About NSError

NSError is used in code built on the Foundation framework for recoverable error situations. By merely receiving an NSError object, you know something went wrong, and you can inspect the object to learn more detail about what happened. A good writeup on NSError can be found at NSHipster.

Unfortunately, constructing and inspecting NSErrors can be cumbersome. To create an NSError, you need to specify a domain and a code. The domain should be a reverse DNS-style string, like “com.company.app”. The code needs to be an integer that’s unique within the error domain. The domain and code tend to be of limited usefulness when using NSErrors later, but you need to supply them when creating NSError objects anyway.

The more-often useful information in an NSError lives in its userInfo dictionary. You provide values for a few system-defined keys like NSLocalizedDescriptionKey, and you can attach any additional objects you wish to the same dictionary. I often need to refer to documentation to remember NSLocalizedDescriptionKey, and attaching extra information to the dictionary requires you to keep track of the keys you use in order to consume them later. Picking apart an NSError very frequently involves trips to a header file or framework documentation.

Using Swift enums nicely addresses these complaints.

A Swifty Enum Error

So what does an enum error look like? Here’s an example of a hypothetical error type for interacting with an API server.

enum APIError {
    case NoInternet
    case HTTPError(statusCode: Int)
    case ServerError(message: String)
}

A function which can produce an error could look like this.

typealias APICallback = (data: NSData?, error: APIError?) -> Void
func makeAPICall(callback: APICallback) -> Void {
    // Success
    callback(data: NSData(), error: nil)
    
    // There was no internet connection
    callback(data: nil, error: .NoInternet)
    
    // There was an HTTP error code
    callback(data: nil, error: .HTTPError(statusCode: 404))
    
    // The server rejected the call
    callback(data: nil, error: .ServerError(message: errorMessage))
}

Creating errors becomes easy, clear, and descriptive. It’s even statically-typed, so the compiler will require you to construct the error correctly.

Consuming errors is simple, as well. You could use a switch statement to handle some—or all—cases.

switch(error) {
case .NoInternet:
    println("You should get a better ISP")
case .ServerError(let message):
    println("The server responded with message: \(message)")
default:
    // Handle other error cases in a general way
}

Often you just want a textual description of the error. You might want to put the text into an alert view, or you might want to log out the error description while debugging. This can be handled by having your error type implement the Printable protocol.

enum APIError: Printable {
    case NoInternet
    case HTTPError(statusCode: Int)
    case ServerError(message: String)
    
    var description: String {
        switch self {
        case .NoInternet:
            return "There is no internet connection."
        case .HTTPError(let statusCode):
            return "The call failed with HTTP code \(statusCode)."
        case .ServerError(let message):
            return "The server responded with message \"\(message)\"."
        }
    }
}

Printing the error then becomes as simple as this.

println(error)

In my time with this style of error, I believe they are far easier to construct and consume than NSError, so I quite like them.

What About Objective-C Compatibility?

Unfortunately, Swift enums can’t export to Objective-C. So what if you want to use these awesome enum errors in Swift, but still need your code to be consumable from Objective-C? In that case, you need to provide a way to translate the enum error to an NSError object, and then export the NSError object to Objective-C.

You can pull this off by adding a foundationError property to your enum error.

enum APIError: Printable {
    case NoInternet
    case HTTPError(statusCode: Int)
    case ServerError(message: String)
    
    private var domain: String {
        return "com.company.app"
    }
    
    private var errorCode: Int {
        switch self {
        case .NoInternet:
            return 100
        case .HTTPError(let statusCode):
            return 101
        case .ServerError(let message):
            return 102
        }
    }
    
    var description: String {
        switch self {
        case .NoInternet:
            return "There is no internet connection."
        case .HTTPError(let statusCode):
            return "The call failed with HTTP code \(statusCode)."
        case .ServerError(let message):
            return "The server responded with message \"\(message)\"."
        }
    }
    
    var foundationError: NSError {
        return NSError(domain: domain, code: errorCode, userInfo: [
            NSLocalizedDescriptionKey : description
        ])
    }
}

Then, in code that can return an error, you must provide overloads that return an NSError.

func makeCall() -> APIError? {
    return .NoInternet
}

func makeCall() -> NSError? {
    return makeCall()?.foundationError
}

The NSError-returning overload will be the only version made available to Objective-C, so it’ll be as if your enum error code doesn’t even exist there.

Since both overloads will be visible from Swift, however, you’ll need to be explicit about the overload you call, instead of letting Swift infer the type.

let error: APIError? = makeCall()

It’s not the best solution, since you have to add extra code to handle codes and domains to the error enum, and you have to use explicit typing when calling from Swift, but it still more or less gets you the best of both worlds.

Of course, the better long-term solution to this problem is to stop using Objective-C altogether and just use Swift full-time :D

Benefits of Swift Embedded Frameworks in iOS 8

October 30, 2014

Since WWDC, I’ve spent a lot of time playing with Swift. I’ve even shipped two apps written in the language: EventBoard IT at work and Maidenhead Converter on my own time.

Overall, I quite like the language, but it’s clearly in an early state. I would like to talk about Swift in greater detail later, but for now I want to focus on one particular issue I’ve had that I only just recently conquered, and some surprising additional benefits that the solution provided.

Compile Times

The Swift compiler is slow. On my MacBook Pro, every 100 lines of code adds a full second to the compile time. EventBoard IT weighs in at about 4,000 lines of code, and takes 42 seconds to compile. When the same machine can compile a 30,000 line Objective-C app in a fraction of the time, that’s unacceptably slow. My much faster iMac compiles EventBoard IT in a third the time, which is better, but still too slow.

This one issue was leading me to postpone doing further significant work in the language, which saddened me. I tried numerous suggestions to improve compile times—explicitly type everything instead of taking advantage of the implicit typing feature, watch the build log to see if any one file was taking a disproportionately long time, etc—but nothing seemed to make a difference.

While the compiler needs to speed up generally, one of the biggest problems it has right now is that it doesn’t delta compile; every time you hit build it will recompile the entire app, and not just the parts that changed since the last build.

The Swift team explained that this is because of the nature of the language. In Objective-C you have explicit #include statements that make it really easy to build a dependency graph, which makes it possible to see which parts of an app might be affected by a change in one source file. Swift doesn’t have that: any source file in a target can see any symbol in any other source file in the same target. Because of that, the compiler can’t easily judge which files might be affected by a change in another file. The Swift team has said they intend to implement dependency analysis in the same way the C# team does, but that doesn’t exist yet, so your entire app recompiles every build.

As it turns out, there’s a way you can take advantage of delta compiles in Swift today: embedded frameworks. While the compiler can’t do dependency analysis inside a target, it can do it between targets. If the code in an embedded framework didn’t change since a previous compile, it doesn’t need to be recompiled.

A few days ago I started pushing parts of EventBoard IT out into embedded frameworks. I’m not done with the process yet, but I’ve already cut the compile time nearly in half: a delta compile takes 24 seconds. Interestingly, a full compile only takes 30 seconds now. I suspect that’s because of a combinatorial explosion effect, where every file you add to your project adds to the compile time of every other file in your project. Since the project is being split into different modules, each module has less to consider than the app as a whole did.

This fix makes me excited to use Swift today again. But as it turns out, that’s not the end of the benefits that embedded frameworks yield.

Public Header View

Header files in languages like Objective-C have one really distinct advantage: you can declare and document your public interface in the header file, while hiding all of the implementation in the implementation file. When done well, you can look at a header file to learn how to use a class, without ever having to look at the implementation. Modern languages don’t have header files, and so everything goes in the implementation file. This is unfortunate, but not so unfortunate that I think header files have any place in a modern language. Swift made the right decision ditching them.

But wouldn’t it be nice if you still had a view in Xcode that just showed you the public interface of your code, with its documentation comments? You get this view when you inspect symbols on any built-in framework like UIKit, but what about your own code?

Embedded frameworks give you this public header view. In source files that use your framework, you include an import statement. If you command-click the framework name in the import statement, you will see the public header view. It shows the documentation comments you wrote and the public symbol declarations, but not your private nor internal symbols, and not the implementations of your public symbols. It’s glorious.

Limited Scope

When you write a view controller, you often write various sub-controllers or views or model objects that are used by the view controller, but not by anything else in the app. Your classes might also have public properties and methods that are used by these near-neighbor classes, but are not relevant to the rest of the app. Unfortunately, even though these classes are only used by the one view controller, their symbols are visible to the rest of the app. Which means that anything else in your app could instantiate or manipulate them.

In my experience, one of the best ways to improve robustness in your software is to limit the scope your code has visibility of at any given moment. Minimize global variables, use const wherever you can, etc. The fewer moving parts your code has access to at any given point, the fewer moving parts it can potentially break or depend on in brittle ways.

Classes are, in a sense, themselves global variables. NSObject can be thought of as a global variable that refers to an object that can be used to construct instances of NSObject. Every class you declare adds another object-constructing global variable to the scope of the rest of your app. A bit unnerving to think of it that way, no? But what can you do? Either hide all of your view controller’s related classes in the same implementation file, or accept that you’re exposing those symbols to the rest of your app. It’s workable, but it kind of sucks.

Swift’s access control support combined with embedded frameworks improves this situation. By default, symbols in a Swift framework are internal, which means they can be seen by anything within the target, but nothing outside it. In an embedded framework, you have to explicitly annotate symbols with public to make them visible outside the target. So you can make a view controller with a host of utility classes and only annotate the view controller itself as public, and then the rest of your classes have no visibility to the rest of the app. Boom, those classes are no longer global variables—at least, not outside their framework.

A related benefit: you’re forced to think more explicitly about exactly which symbols should be public. You ask yourself if something really needs to be public, or which module something really belongs in. It helps push you towards clean interfaces.

I’m Loving Embedded Frameworks

It feels like these new embedded frameworks in iOS 8 are going to result in a significant change in the way I build apps. I’m going to try to continually silo things off in their own framework wherever I can. The compile time complaint will (hopefully) go away with improvements to the compiler, but the public header view and especially the improved ability to limit scope are really substantial benefits.

Maidenhead Converter 2.0

September 30, 2014

Tonight, I’m bringing Maidenhead Converter back to the App Store.

I pulled all my apps off the App Store some time ago (was it last year? Or the year before? I forget.) I would like to start getting improved versions of them back up on the store, beginning with Maidenhead Converter.

2.0 is a complete rewrite and redesign of the app. A lot has changed since 2009, including a new OS design in iOS 7 and new 4, 4.7, and 5.5 inch iPhones. Also the iPad happened, but I’m not quite optimizing for that just yet.

I had a number of feature requests for the original app that I never implemented. Folks wanted different coordinate formats—added!—a map in the app—added!—and the ability to get the heading between two points—not added yet, but give me time! I would also like to add features like a Maidenhead grid overlay on top of the app.

Technologically, I wanted to experiment with Swift and Storyboards. I’ve also used Swift in a larger app for work, and I would like to write my thoughts on the language in detail later. Long story short: I like it, but the compiler is really not ready for prime time yet. Storyboards worked out fine, but it was a small app and I’m not yet entirely sold on the idea of using Storyboards with a large app. Maybe if you split into multiple Storyboards. I need to think on it and experiment more.

When Maidenhead Converter was a paid app, it never made much money. After I set it free, it got plenty of downloads, but obviously stopped making money. This time around I’ve decided to experiment with iAd. The app now shows banner ads on the main screen. If you dislike the ads, you can purchase a $1.99 ad disabler to turn them off. I don’t expect to make any serious money on the app, but maybe it’ll earn enough for some nice food every now and then.

Maidenhead Converter 2.0 requires iOS 8. Download it now!

4 Years, 5 Months, 4 Days

September 14, 2014

On April 10, 2010, I put my site into a dormant state. I haven’t written a blog post since.

Every few months, I would try rewriting my site, but then lose interest and put the project down again. The last live version of my site was written in PHP and hosted on GoDaddy. My first rewrites were in PHP on WebFaction (which I recommend over GoDaddy). Then I experimented with Node.js (which I recommend over PHP) on Azure. Now finally, what you see is a static site generated by Jekyll and hosted on GitHub Pages.

The workflow for writing posts in this new setup is more command-line and git-commit centered than I would prefer, but it does come with the advantage that I can add new CSS and images and whatever else I need in the same commit, and have it all go live at the same time. I also frequently use my personal site to host little experimental prototype projects, and getting those online will be easier now.

There’s been much I’ve wanted to write about. I’ve been tweeting about Swift an awful lot, but blog posts are a much better place to show code than PNGs. I’ve also long wanted to write about my approach to app architecture on iOS. On occasion I buy new hardware and want to write a review about it. Now I can.

So, I’m back, and hopefully my next post won’t take four years.

Newer