Swift Package Import in Objective-C Might Just Work

My recent posts were about SwiftUI quirks and annoyances, so I want to share a short success story for a change:

You can @import Swift Packages in Objective-C code just fine. (At least if they are Objective-C compatible.)

How I Got Here

I updated my WordCounter app to rely less on Carthage and use Swift packages where possible instead.

The WordCounter’s dependencies are simple in comparison to my other apps, so I merely had to update a handful of sub-projects to expose a Swift Package manifest, bump some deployment targets, and that was it.

When I began deleting the pertinent lines from the Cartfile and began to add the package paths to Xcode, I realized that the WordCounter’s graphs are still drawn from Objective-C NSView subclasses that use CorePlot. I also use CorePlot in one of my internal modules. Wouldn’t this cause all kinds of trouble?

In hindsight, I feel stupid for even trying the following, but in the spirit of sharing, enjoy what won’t work:

I kept the CorePlot entry in the Cartfile. This compiled the CorePlot.xcframework, and I linked and embdeded this framework in the app so Objective-C would be able to #import <CorePlot/CorePlot.h>. I also kept the Swift Package managed CorePlot dependency from my internal module I just updated. The linker wasn’t happy about the confusion, of course, and the app wouldn’t run. After sitting on this for a day or three, I recalled that I could import the Swift Package managed CorePlot module in a .framework target and use this as an “umbrella” framework! So I researched how to best approach this. Instead, I found that people complained about Objective-C files in Xcode 11 not importing Swift Package modules correctly. Which implies it should.

So today, I replaced #import <CorePlot/CorePlot.h> with @import CorePlot; in a handful of Objective-C files and that’s it. No fancy workarounds needed. Sweet!

Objective-C Compatibility

Fortunately, CorePlot itself is written in Objective-C. The Package manifest doesn’t even look funny: .h and .m files just work as expected. (Well, I didn’t expect that, but may you do.)

From what I gathered, Xcode will generate Objective-C header files from Swift Packages, if possible, to make @import statements work. The imported Swift module needs to expose @objc types for this to work, though, and this means you can’t use enums with associated values etc. But in principle, importing a Swift Package in your app’s Objective-C files should work.

That’s great news, and it saved me a lot of headaches!