Fixing Random Errors Caused by Importing a Swift Bridging Header

We’ve encountered build errors sporadically while importing our module’s Swift header into Objective-C. These mysterious errors were seemingly unrelated to the code we were working on. SESliderTableViewCell won’t build because I imported GC-Swift.h into GCAlerts.m. What on earth is going on? To make it even more confusing, we’ve been able to “fix” the error by […]

We’ve encountered build errors sporadically while importing our module’s Swift header into Objective-C. These mysterious errors were seemingly unrelated to the code we were working on. SESliderTableViewCell won’t build because I imported GC-Swift.h into GCAlerts.m. What on earth is going on?

To make it even more confusing, we’ve been able to “fix” the error by creating a new Objective-C class in our project and importing GC-Swift.h over there. We used this technique to create shims around new Swift classes that needed to talk to older Objective-C code in our app with some success.

A screenshot of Xcode showing errors after importing the Swift briding header

Breaking It Down

It turns out that the connection between these files and classes, although non-obvious is actually very logical. The build time errors that Xcode gives us tells us what’s failing to build, and if we look closely, we see that as part of building GC-Swift.h, Xcode is building AppFriendsUI, which is in turn building SESlideTableViewController.

A screenshot of the failing build, highlighting AppFriendsUI as the point of compilation failure.

Xcode is building all of these things, because, as part of importing our swift bridging header, it needs to be able to link against anything and everything that goes into the header. Anything and everything in the bridging header, as it turns out, is quite a lot.

Something interesting is happening here: When we import the bridging header into non-ARC code, the imported header is treated as if it’s non-ARC code too. (This makes sense, because header imports are just replaced with code by the pre-processor at compile time.) As a result, those warnings are emitted, causing our project to fail to build. A failure in AppFriendsUI should cause GC-Swift to fail to build, but why did AppFriendsUI not compile? (Spoiler: And why should warnings cause a failure?)

Since AppFriendsUI is its own Swift module, it gets its own bridging header. If we look at AppFriendsUI-Swift.h, we’ll see that SESlideTableViewCell is failing to compile too. If we examine SESliderTableViewCell, we see a few errors about property attributes being missing or incorrect.

Recall that property attributes are the keywords like nonatomic and strong that go in parenthesis as part of a property decleration. A warning about incorrect property attributes something that I haven’t seen in a long time, so let’s look it up online… As it turns out, prior to automatic reference counting (ARC), the compiler would warn us about missing memory management property attributes.

Taking Things Seriously, Sometimes…

You might be wondering, though: If Xcode warns about missing memory management attributes, shouldn’t our app still build? Of course, warnings don’t usually cause the compiler to fail, but as part of our project we’ve told Xcode to treat warnings as errors. So, any pre-ARC code in our project would fail to compile with missing or incorrect memory-management property attributes.

A screenshot of Xcode settings, showing treat warnings as errors.

So why does our app only fail some of the time after importing GC-Swift.h? Only some of our source files are set to compile with ARC turned off. We have it enabled by default, but there’s a flag we set in our target’s build settings on a per-file basis: -fobjc-no-arc. If any of those files imported the offending code, the error would surface.

Building It Back Up

By now we’ve seen a random compiler error, figured out what was causing it, and why it only happened sometimes. Let’s talk about a fix. In diagnosing the problem, we talked about abstracting out the code into another module, and as we discussed earlier, using a wrapper class. We could also try editing the pod generating the errors, but if we need to upgrade it, those fixes would get blown away.

These approaches work around the symptoms of the problem by abstracting away build errors to somewhere else, so they don’t get in our way. It turns out that there’s a better way to fix the root cause of the problem: Upgrade to ARC as we encounter files suffering from this problem.

After changing GCAlerts.m to compile with ARC, we were able to build and run our app with no problems.

To summarize:

  1. We were encountering random build errors when importing our Swift bridging header into some Objective-C files.
  2. Importing a Swift bridging header into Objective-C imports all of the Swift classes and all of their dependencies.
  3. Before ARC, the compiler warned about missing memory-management property attributes.
  4. We have treat-warnings-as-errors enabled.
  5. Any non-ARC class in our project missing memory-management property attributes would fail to compile (Steps 3 & 4)
  6. One of our dependencies was missing memory-management property attributes.
  7. That dependency is getting pulled into whatever classes import the Swift bridging header. (Step 2)
  8. Some of our Objective-C files are still being compiled without ARC.
  9. Any of those non-ARC Objective-C classes that import the Swift bridging header would fail to compile.
  10. By converting non-ARC files to ARC, those warnings would no longer be emitted, nor promoted to errors. (Inverse of Step 5)

The solution is therefore to convert those files to ARC.

Addendum: Converting Individual Files to ARC

If you search the web for ARC conversion, you’ll see that there’s a tool built into Xcode to do that. We have a mixed project, though, and we don’t want to convert everything at once. So if you need to convert just one or two files to ARC in a mixed project, here’s how you do it:

A screenshot of the build settings screen with captions describing how to enable automatic reference counting

  1. Click on your project in the project explorer.
  2. Click on the target that includes the files you want to convert.
  3. Click in “Build Phases” Phase
  4. Click on Compile Sources and find the file you want to convert.
  5. Delete the -fno-objc-arc flag
  6. As a final step, you need to go into the file and remove any calls to retain, release, and autorelease.

Source: GameChanger