Donald Hays

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.