Simplifying Swift framework development

I’ve developed a handy trick when writing frameworks in Swift that makes the overall process a little bit nicer, and it’s just adding a single file to your framework.

Let’s say you’re building CoreAwesome.framework for inclusion in your app, or publishing to Github, or whatever. There are a couple things you end up doing a lot:

First, you end up having lots of import OtherFramework statements scattered throughout your .swift files, so that portions of your framework can have access to pieces of functionality provided by dependencies (whether system frameworks or whatever). I find that sort of repetition (import Foundation, anyone?) to be pretty annoying.

Second, you don’t always have a good way of getting access to the Bundle that corresponds to the framework easily. You need this particularly if you’re loading bundle-specified resources.

So, based on these two things, I’ve developed this pattern, which is kind of like a swift framework pre-compiled header:

When I create CoreAwesome.framework, I get CoreAwesome.h, and that’s about it. So I immediately add CoreAwesome.swift at the top level next to the .h, and put this in it:

// CoreAwesome.swift
@_exported import Foundation
@_exported import DependencyA
@_exported import DependencyB

public let CoreAwesome = Bundle(for: CoreAwesomeMarker.self)

private class CoreAwesomeMarker { }

First, there’s this weird @_exported thing. The underscore indicates we need to be a bit wary of it, because it’s not a modifier you’re really supposed to use. But if you do…

@_exported will make an import-ed module visible to the entire module into which its been imported. This means you don’t have to import Dependency in every file you need it. You just @_exported that dependency once, and you’re good to go in any file in that module.

This is especially nice if DependencyA defines public operators, which aren’t always imported the same way that symbols are. That’s a topic for another day.

Second, I define a public constant that is the name of the framework, and whose value is the Bundle for that framework. I use the class-based look up (ie, find the bundle that declares this class), because it’s one of the few convenient Bundle initializers that doesn’t return a Bundle?, and thus I don’t have to deal with unwrapping. And then I use a special marker class for making that lookup resilient in the face of other functionality changes.

With the constant in-hand, I can easily load resources:

let resource = CoreAwesome.url(forResource: "Foo", withExtension: "plist")

This reads pretty naturally, and the entire file makes developing my framework just a little bit easier.

Update: I neglected to mention that it was Joe Fabisevich who clued me in to the @_exported trick. Thanks, Joe!!

Update #2: Both Harlan Haskins and Kevin Ballard pointed out that the constant to access the framework’s bundle will conflict with a module-qualified declaration. Like, if you have two modules that declare a Foo, then you need to disambiguate which one you want by doing Module1.Foo vs Module2.Foo. However, if Module1 is the name for a Bundle instance, this breaks.

Solving this problem is left as an exercise for the reader. 😉

Leave a Reply

Your email address will not be published. Required fields are marked *