Facilitating Idiomatic Swift with Objective-C


tl;dr If you are hitting roadblocks in trying to communicate with Core Foundation and C APIs directly from Swift, just wrap them in a friendly Objective-C class that provides a simple API for the rest of your application to use.
Swift logo

At Meta, most of the work that has gone into our client applications thus far facilitates keeping our user’s files in sync with our cloud system so that they can be easily searched and accessed. When we began work on the client application for Mac OS X a few months ago, there were two major decisions that had to be made. The first was how to organize the application so that a resilient background process could listen for file system changes and keep everything up to date in the cloud. I’ll likely be discussing that in a future post. The second decision was what language to base the project in, which I will be discussing here.

Since I was to be the primary developer of this application for the short term, this was partly a personal choice. While most of my experience in Apple’s world to date had been in writing Objective-C code, I had experimented with Swift and was excited to try it out in a production application. The were a few features in particular that I was attracted to in Swift.

To start with, Swift’s concision was especially enticing. The ability to express oneself quickly is a desirable feature in any language, and fewer lines of code written means fewer lines of code to maintain. Another area of interest was the type safety, which Swift makes available through its data structures. Our system is largely event-based, with relatively complex data types passing information around about the state of a user’s files. In the back end, our use of structs provided by Go provides strong type safety in handling incoming data. Being able to work with similar guarantees on the client side is highly desirable. A third aspect that was appealing to me personally was the functional aspects of Swift. I enjoy programming in a functional style and Swift’s built in functionality for map, reduce, join, and the like were a great stimulus to try out the language further.

Once the decision was made to use Swift as the primary language, development of our Mac application moved forward rapidly. Once the various components of the application were in place and structured, I began work on the meat of the application: handling changes to a user’s files. To interface with the file system on the level we required, much of the interaction occurs within the Core Foundation frameworks and in pure C code. As we tried to interface with these frameworks in Swift, a variety of issues came up.

Interfacing with UTType

Critical to analyzing a file’s content and displaying a meaningful description of a file to the user is knowing what kind of file you are dealing with. Cocoa’s UTType functionality provides us with the ability to extrapolate a file’s MIME type for use in our cloud services.

Swift

In Swift, my best attempt at interfacing with UTType is as follows in the code snippet below. There are two calls into the UTType framework. The first requires a parameter of type CFStringRef, which is easily created with a cast which utilized toll-free bridging behinds the scenes. The return value of this function is Unmanaged<CFString>!, indicating that is an unannotated method where the memory management must be manually handled in Swift. The takeRetainedValue() function is used because the value returned by this function as already been retained and needs to be released when the current scope is exited, thus removing the Unmanaged<> wrapper from the inner type. Additionally, the failure case I have here is rather convoluted because it occurs when the cfMimeType fails to bridge to a string, rather than explicitly from the return of the function call.

// this code crashes at runtime
public class func mimeTypeForFile(path: String) -> String {
    let ext = path.pathExtension as CFStringRef
    let UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, nil).takeRetainedValue()
    
    let cfMimeType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType).takeRetainedValue()
    if let mimeType = cfMimeType as? String {
        return mimeType
    }
    return "application/octet-stream"
}

Once I got this code compiling, I wrote a short unit test. Unfortunately when this code is run the following error occurs, a dreaded EXC_BAD_INSTRUCTION runtime error. It is certainly possible that this code could be further massaged to get this example working (please let me know if you are able to), but after a good half hour or so I wasn’t able to find a solution.

Swift UTType interfacing

Objective-C

Looking at the same code in Objective-C, several things are more desirable. Firstly, this code works; usually a desirable attribute in production code. No bad instructions errors here. Secondly, coming from a background of Objective-C, what is happening here is clearer than in the Swift code. Rather than dealing with wrappers on Core Foundation types meant to insulate Swift from pointers, you are dealing with the implementation directly and the underlying interactions are clearer. Since most developers writing Swift are coming from an Objective-C background, it should be explicitly clear what this code is doing in regard to memory management and error handling.

- (NSString*) mimeTypeForFile:(NSString*) path {
    CFStringRef ext = (__bridge CFStringRef)[path pathExtension];
    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL);
    CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
    CFRelease(UTI);
    if (!mimeType) {
        return @"application/octet-stream";
    }
    return CFBridgingRelease(mimeType);
}

File System Events

Another area where we ran into issues was in our use of the File System Events API, which provides a stream giving callbacks to your application when anything changes on the file system within a specified scope.

What follows is my best attempt to create File System Event stream in pure Swift. There is a lot of syntactic baggage surrounding pointer handling and Core Foundation types, but theoretically all that should work just fine. The blocking issue is that a CFuntionPointer, Swift’s type which is analogous to a normal function pointer in pure C code, cannot be created from either a Swift function or a closure. This is a limitation of the Swift language (until the recent 2.0 release, more on that later). The following code fails to compile because of this limitation.

// this code does not compile
func testCreateSimpleSwiftListener() {
    let cfDirs = [NSHomeDirectory()] as CFArray
    let context = nil as UnsafeMutablePointer<FSEventStreamContext>
    let latency = 3.0 as CFAbsoluteTime
    let flags = kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagWatchRoot
    
    let callback: @objc_block (
    ConstFSEventStreamRef,
    UnsafeMutablePointer<Void>,
    Int,
    UnsafeMutablePointer<Void>,
    UnsafePointer<FSEventStreamEventFlags>,
    UnsafePointer<FSEventStreamEventId>) -> Void = {
        (streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds) -> Void in
        NSLog("Received %i paths", numEvents)
        // handle file event
    } as CFunctionPointer
    
    let stream = FSEventStreamCreate(
        kCFAllocatorDefault,
        callback as FSEventStreamCallback,
        context,
        cfDirs,
        kFSEventStreamEventIdSinceNow,
        latency,
        flags
    )
    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)
    FSEventStreamStart(stream)
}

Here is the code with the compiler errors shown.

Swift FSEvents interfacing

Again, with some further massaging this might or might not be possible by delving further into the Objective-C runtime, but in doing so we yet again lose out on the most of the benefits of working in Swift at all. Even with this function pointer issue fixed, the UnsafeMutablePointer<Type> and UnsafePointer<Type> wrappers are uncomfortable to work with and a jarring change from the kind of Swift code that one can enjoy writing.

Reclaiming the benefits of Swift

In the previous two examples, nearly all of the reasons that I chose Swift for this project in the first place have been eroded to some degree. With the wrappers around low level objects, much of the concision disappeared. Since we are using Core Foundation types, type safety within data structures has withered. Lastly, higher level language features can only be applied after these low level types have been sufficiently dealt with. It basically feels like writing C code with more verbose and unfamiliar syntax.

Ideally, when we are writing Swift we can focus on the benefits of the language, not how it interfaces with the very code it was meant to replace. The solution we ended up using here is to wrap interactions with Core Foundation and C APIs with simple Objective-C wrapper classes that provide a clean API for use by your Swift code. For us, this consisted primarily of two basic classes, FileSystemListener and FileSystemCrawler. The listener class is a wrapper around code dealing with the aforementioned File System Events API, and the crawler wraps low level C code with interacts with the file system as efficiently as possible, enabling introspectable breadth-first crawls that can handle concurrent modifications to the file system.

By providing these interfaces, the rest of our Swift code can interact with a purpose built API that dovetails nicely into our business logic, written in Swift, feeding changes to our events streams in the cloud. Additionally, it helps to reinforce good design principles in the realm of code modularity and separation of concerns, making your application more maintainable and testable moving forward.

Objective-C and Swift

Interop between Objective-C and Swift at a higher level is a much friendlier affair, and most developers working in Swift probably have some experience in this area. Still, at the risk of paraphrasing the documentation, there were a few areas which were of particular interest to us that I would like to touch on. If this is an area where you are working in-depth, I would recommend that you to consult Apple’s documentation on Using Swift with Cocoa and Objective-C directly for a much more thorough treatment of the topic.

Bridging

The basis for interop between Objective-C and Swift is bridging between the two languages. Calling into Objective-C from Swift requires a bridging header to be inserted into your project, which Xcode will usually prompt you for, as shown below, when an Objective-C file is added to a Swift project. This file is linked to your build settings in the Swift Compiler - Code Generation section as the Objective-C Bridging Header. This header file contains import statements for the header files of Objective-C code that you wish to interact with. The Objective-C file that prompted its creation will be automatically added, and all subsequent files must be added manually.

Syntactic Translation

As documented, the Swift compiler performs translations of Objective-C syntax into Swift so that initializers, methods, and function calls can be performed natively from Swift code. Most of these conversions are fairly intuitive, and you have probably worked with some examples of these translations. The below example is a simplified version of a delegate of our FileSystemListener class that wraps the FSEvents library using Objective-C.

@protocol FileSystemListenerDelegate <NSObject>
-(void)fileCreateEvent:(FSEventStreamEventId)eventID atPath:(NSString*)path withFlags:(FSEventStreamEventFlags)flags;
-(void)fileModifyEvent:(FSEventStreamEventId)eventID atPath:(NSString*)path withFlags:(FSEventStreamEventFlags)flags;
-(void)fileRemoveEvent:(FSEventStreamEventId)eventID atPath:(NSString*)path withFlags:(FSEventStreamEventFlags)flags
@end

This stub of a Swift class demonstrates how the syntax translates between the two languages. Perhaps the translations are a bit more verbose than they could be, but it is still perfectly understandable and functional. And of course, Xcode’s auto complete functionality will fill out the translated function as you type.

class FileHandler: FileSystemListenerDelegate {
    func fileCreateEvent(eventID: FSEventStreamEventId, atPath path: String!, withFlags flags: FSEventStreamEventFlags) {}
    func fileModifyEvent(eventID: FSEventStreamEventId, atPath path: String!, withFlags flags: FSEventStreamEventFlags) {}
    func fileRemoveEvent(eventID: FSEventStreamEventId, atPath path: String!, withFlags flags: FSEventStreamEventFlags) {}
}
Initializer behavior

One behavior to be cognizant of in this translation is the handling of Optional types from potentially failable Objective-C initializers. Since Objective-C does not have a concept of Optionals, the default translation is auto-unwrapped Types, indicated with a bang at the end of the type, e.g. Type!. Some APIs are annotated to indicate whether or not the initialization can fail, either eschewing an Optional wrapper of prompting the use of a normal Optional. For those that are not, the way auto-unwrapped Optionals work is that when accessed, the underlying value is implicitly unwrapped, causing a panic if the underlying value happens to be nil. If you are coming from an Objective-C background where we can happily send messages to nil objects and move right along, this is definitely something to watch out for. As usual, this behavior is well documented within Apple’s treatment of the topic.

Data Types

One area of concern in designing an interface between Objective-C and C code is the loss of type information for data structures. As I mentioned previously, one of the most attractive features in Swift initially was preserving type information, as opposed to untyped NSArray and NSDictionary objects. In the current release of Swift, these are still issues, but with a bit of care they can be handled gracefully.

The following code snippet shows a simple example of optionally casting a NSDictionary into a typed Swift dictionary. The collection classes documentation has further examples.

let d = NSDictionary(object: "value", forKey: "key")
if let data = d as? [String: String] {
    print("data: \(data)")
}

A very simple example of bridging between the NSString and String types by casting. When working with strings, methods for both NSString and the builtin Swift String types are available. Yet again, you can checkout the strings documentation for further examples.

let n = "my string" as NSString
let s = n as String

Cocoapods

We use Cocoapods for third party dependency management in the Meta client application. When experimenting with pods that are written in Swift, we came across an interesting aspect of the tool that I had previously been unaware of. Cocoapods has historically used static libraries, because while Mac has supported dynamic libraries for some time now, iOS has only supported them since the release of Swift. Static libraries work well for Objective-C, but as described by a blog post on the release of Cocoapods 0.36, dynamic libraries are a requirement for Cocoapods written in Swift. The use of dynamic libraries is enabled by a use_frameworks! line in your Podfile specifying that your pods should be dynamically linked. If you have further interest in this topic, their previous blog post further illustrates the distinction.

A Lingering Issue

For the most part, when you move up out of interactions with Core Foundation and C code, interop between Objective-C and Swift just works. However, somewhere along the line we introduced a crash of the Swift compiler when building for release with optimizations, which is fixed by turning off the compiler optimizations, setting Optimization Level to -Onone in the Swift Compiler - Code Generation section. When I gave this talk at Boston Swift, an audience member brought up a twitter discussion pointing to an annotation which may be useful in tracking down this bug. I’ll be diving into this in the coming weeks as we approach our closed beta launch, and if a solution is found I’ll post an update. Compiler optimizations can make an orders of magnitude difference in the performance of a program, so turning them off completely is quite undesirable.

Conclusions

When working with a new language, the last thing you want to do is try to code in the style of another language in a way that is non idiomatic. As a general rule of thumb, approaching a new language that way almost always results in less performant and less elegant code. In Swift, a lot of effort has been put in to ensure that developers can continue to interact with the existing C-based ecosystem surrounding Apple’s developer tools. While these tools are very useful for certain situations, they are far from a panacea where everything can be done in Swift. Choose your tools for the problem at hand, and when it comes to Mac and iOS development, that can mean thoughtful separation of concerns that allows you to have Swift and Objective-C existing within a simple application in a way that plays to the strengths of each language.

===

Updates after WWDC 2015

The development work underlying this blog post all took place before the 2015 WWDC conference last week. Two changes in particular help with some of the issues that are described above and I am very excited about trying them out within our application. With our preparations for the beta launch I have not yet had time to do so, but I’ll give my first impressions about the benefits they present.

Objective-C Generics

One of the ongoing concerns I had with using Objective-C as opposed to Swift was the lack of type safety in data types. This has always been the status quo with Objective-C, but with the latest release of the Xcode 7 beta, support has been added for Objective-C generics, which will translate into typed Swift data structures. This is an exciting new feature and a great indicator that Apple is still putting significant effort into the development of Objective-C as well as Swift.

Lightweight generics allow you to specify type information for collection classes like NSArray, NSSet, and NSDictionary. The type information improves Swift access when you bridge from Objective-C and simplifies the code you have to write. (6294649)

CFunctionPointer support

The central issue that blocked our efforts to use pure Swift was a lack of support for the CFunctionPointer type. With the newest Xcode 7 beta release, support for function pointers has been added.

Native support for C function pointers: C functions that take function pointer arguments can be called using closures or global functions, with the restriction that the closure must not capture any of its local context. (16339559)

This is an exciting change that would theoretically enable us to port the File System Events interactions to Swift. In combination with generics, many of my primary concerns have been alleviated. However, I think that the benefits of creating wrapper classes around these lower level APIs still hold. By abstracting away the complexity of the underlying functionality and providing a higher level API specific to your application, your code is more modular and more easily understandable. As Apple seeks to fortify Swift’s abilities, many of the issues I have run into will be undoubtedly be fixed. While the choice of language may fall more to the developer’s preference in the future, the principles of modularity will always apply.

As we grow our team here at Meta, we want to make it as easy as possible for our newest team members to get up to speed and shipping code to our customers. If you’re interest in helping us simplify the complex and unsolved problem of finding files, reach out to us at careers@meta.sc

Slides

This blog post is based on a talk I gave for Boston Swift. The slides are up on Slideshare.

Further Reading