Using Linux C APIs in Swift: Glob

In order to function as a systems language, we need an easy interface to the primitives on a system. On the BSD-like OS X, that means using the myriad BSD functions found in /usr/share/man/man3 (well, the lower-case ones mostly). In this post, I'll show how to wrap glob(3) with Swift, since I needed it for a recent project and it's a reasonably simple introduction to Swift-C interaction.

The glob API

On OS X, you can access the manpage via $ man 3 glob. The manpage shows three different functions you can call, though one of them (glob_b) uses a block rather than a function pointer, so I'm not sure whether that's going to make the port over to Linux. In any case, let's focus only on glob and globfree.

The glob function

The glob function, as the manpage indicates, has 4 arguments and an error-indicating return value. The arguments are, in order: the pattern, some behavior flags, an error function pointer, and the pass-by-reference result object. The return value is non-zero for errors, so our implementation will just pretend it didn't find any results upon error.

This doesn't quite compile, because we need to adhere to the CollectionType protocol. We can see, though, that we've successfully wrapped glob and globfree in Swift. We'll take care of that gross exclamation point in the string decoding and the storing of results in a minute, but first, why are we adhering to the CollectionType protocol?

The CollectionType Protocol

There are a number of enumerable protocols in Swift, including SequenceType and CollectionType. In our case, it makes more sense to say we want a "collection" of paths rather than a "sequence" of paths. To me, a sequence means that once we've received an element in a sequence, we'll never need it again. I can anticipate use cases where that might not be true, so that's why I'm using CollectionType. Why do this? Once we adhere to either SequenceType or CollectionType, we get for...in functionality for free. Think of it as NSFastEnumeration for Swift.

To adhere to the CollectionType protocol, we need to provide two properties—startIndex and endIndex—and a subscript. We can do that like this:

Adding a Little Safety

Since we're interacting with a C API, we're faced with things like char arrays, which aren't exactlyStrings, since they don't have an encoding. With this as a potential (though extremely unlikely) scenario, we want to extract the data from our glob_t object, sanitize it, and store it in a [String]. We also want to guard against weird input so we can get rid of the exclamation point in our initializer:

Conclusion

With the right techniques, you can abstract away C APIs in your Swift code, making your entire codebase safer and more idiomatic. It's true that glob is a fairly simple API, but the primitives afforded us by the Swift language and the Foundation framework let us design some awfully clean abstractions.