Category Archives: Xcode

Conditional Xcode Build Settings

In the previous post, I described a problematic warning introduced by the new linker in Xcode 15. In it, I shared that the warning can be effectively disabled by passing a suitable argument to the linker:

OTHER_LDFLAGS = -Wl,-no_warn_duplicate_libraries

This simple declaration will address the problem on Xcode 15, but on Xcode 14 and earlier it will cause a link error because the old linker doesn’t recognize the argument. What we want to do if the project will continue to be built by both older and newer versions of Xcode, is to effectively derive a different value for OTHER_LDFLAGS depending on the version of Xcode itself. (Maybe more correctly, depending on the version of the linker, but for this example we’ll focus on varying based on Xcode version).

There’s no straightforward way to avoid this problem, but Xcode build settings offer a sophisticated (albeit brain-bendingly obtuse) mechanism for varying the value of a build setting based on arbitrary conditions. Technically this technique could be used in Xcode’s build settings editor, but because of the complexity of variable definitions, it’s a lot easier (not to mention easier to manage with source control) if you declare such settings in an Xcode configuration file. The examples below will use the declarative format used by these files.

The key to applying this technique is understanding that build settings can themselves be defined in terms of other build settings. For example:

OTHER_LDFLAGS = $(SUPPRESS_WARNING_FLAGS)
SUPPRESS_WARNING_FLAGS = -Wl,-no_warn_duplicate_libraries

These configuration directives have the same effect as the single-line definition above, because Xcode expands the SUPPRESS_WARNING_FLAGS setting and uses the result when setting the value of OTHER_LDFLAGS. Even more powerfully, you can nest build settings so that the value of one build setting is used to construct the name of another:

SHOULD_SUPPRESS = YES
OTHER_LDFLAGS = \((SUPPRESS_WARNING_FLAGS_\)(SHOULD_SUPPRESS))
SUPPRESS_WARNING_FLAGS_YES = -Wl,-no_warn_duplicate_libraries

Here, the final value for OTHER_LD_FLAGS will only include the specified flags if the SHOULD_SUPPRESS build setting is YES, because expanding SHOULD_SUPPRESS yields the build setting SUPPRESS_WARNING_FLAGS_YES. If SHOULD_SUPPRESS is NO, or any other value, then the expansion will lead to an undefined build setting, and thus will substitute an empty value.

Side note: when editing Xcode configuration files, keep one editor pane open to the configuration file, and one pane open to the Xcode build settings interface, focused on a project or target that depends on the configuration file. As you edit and save the file, Xcode updates the derived build settings in real time so you can check your work. For example, if you were to change the value of SHOULD_SUPPRESS to NO, you would see the “Other Linker Flags” value change to empty in Xcode.

Obviously, hard-coding SHOULD_SUPPRESS to YES as we did above isn’t going to solve the problem, because these configuration settings will cause the incompatible linker parameter to be passed on Xcode 14 and earlier.

The simple ability to nest build settings leads to a huge variety of clever tricks that allow you to impose a kind of declarative logic to settings. For example, here’s a cool technique I learned from the WebKit project:

NOT_ = YES
NOT_NO = YES
NOT_YES = NO

NOT_ equals YES? What does it meeeeaaan? It only makes since when you consider what happens when you combine NOT_ with another build setting that is defined as a boolean value:

SHOULDNT_SUPPRESS = \((NOT_\)(SHOULD_SUPPRESS))

This expands to $(NOT_YES) which in turn expands to NO. You can make sense of these exotic uses of nested build settings by slowly walking through the expansion, from the inside out. Each time you expand the contents of a $() construct, you end up with text that combines with the adjacent text to yield either a new build setting name, or the final value for a setting.

Finally, let’s apply a similar trick to the question of whether we’re running Xcode 15 or later. For this I am also leaning on an example I found in the WebKit sources. By declaring boolean values for several Xcode version tests:

XCODE_BEFORE_15_1300 = YES
XCODE_BEFORE_15_1400 = YES
XCODE_BEFORE_15_1500 = NO

We lay the groundwork for expanding a build setting based on the XCODE_VERSION_MAJOR build setting, which is built in:

XCODE_BEFORE_15 = \((XCODE_BEFORE_15_\)(XCODE_VERSION_MAJOR))
XCODE_AT_LEAST_15 = \((NOT_\)(XCODE_BEFORE_15))

In this case, on my Mac running Xcode 15.1, XCODE_BEFORE_15 expands to XCODE_BEFORE_15_1500, which expands to NO. XCODE_AT_LEAST_15 uses the aforementioned NOT_ setting, expanding to NOT_NO, which expands to YES.

Easy, right?

Putting it all together, we can return to the original example with SHOULD_SUPPRESS, and replace it with the more dynamic XCODE_AT_LEAST_15:

OTHER_LDFLAGS = \((SUPPRESS_WARNING_FLAGS_\)(XCODE_AT_LEAST_15))
SUPPRESS_WARNING_FLAGS_YES = -Wl,-no_warn_duplicate_libraries

OTHER_LDFLAGS expands to SUPPRESS_WARNING_FLAGS_YES on my Mac running Xcode 15.1, but on any version of Xcode 14 or earlier, it will expand to SUPPRESS_WARNING_FLAGS_NO, which expands to an empty value. No harm done.

I hope you have enjoyed this somewhat elaborate journey through the powerful but difficult to grok world of nested build settings, and how they can be used to impose rudimentary logic to whichever settings require such finessing in your projects.

Xcode 15 Duplicate Library Linker Warnings

Apple released Xcode 15 a couple weeks ago, after debuting the beta at WWDC in June, and shipping several beta updates over the summer.

I’ve been using the betas on my main work machine, and for months I’ve been mildly annoyed by warnings such as these:

ld: warning: ignoring duplicate libraries: '-lc++'
ld: warning: ignoring duplicate libraries: '-lz'
ld: warning: ignoring duplicate libraries: '-lxml2'
ld: warning: ignoring duplicate libraries: '-lsqlite3'

You may have run into similar warnings with other libraries, but warnings about these libraries in particular seem to be very widespread among developers. Even though I’ve been seeing them all summer, and have been annoyed by them, I made the same mistake I often make: assuming that the problem was too obvious not to be fixed before Xcode 15 went public. Alas.

So what happened in Xcode 15 to make these suddenly appear, and why haven’t they gone away? The root of the problem is not new. Something about the way Xcode infers library linkage dependencies has, for several years at least, led it to count dependencies from Swift packages separately from each package, and to subsequently pass the pertinent “-l” parameter to the linker redundantly for each such dependency. In other words, if you have three Swift packages that each require “-lc++”, Xcode generates a linker line that literally passes “-lc++ -lc++ -lc++” to the linker.

So why have the warnings only appeared now? One of the major changes in Xcode 15 was the introduction of a “completely rewritten linker”, which has been mostly transparent to me, but which was also to blame for an issue with Xcode 15 that prevented some apps from launching on older macOS (10.12) and iOS (14) systems. That bug has been addressed in the Xcode 15.1 beta 1 release, which was released this week.

Another change in the new linker was the introduction of a new warning flag, on by default: “-warn_duplicate_libraries”. Kinda obvious, isn’t it? It’s the new warning flag on the linker that is leading this old Xcode behavior to suddenly start spewing unwanted warnings.

Suppressing the Warnings

Luckily, in conjunction with the new warning flag, the linker also introduced a negating flag: “-no_warn_duplicate_libraries”. If you pass this flag to the linker, the warnings go away. When Xcode links your target, it actually invokes “clang”, which in turn invokes the linker (“ld”). So to see that the flag gets passed down to the lower lever linker, you need to specify it as a parameter to clang that instructs it to propagate the parameter to ld:

OTHER_LDFLAGS = -Wl,-no_warn_duplicate_libraries

OTHER_LDFLAGS is the raw name for the build setting that appears in Xcode as “Other Linker Flags”. The excerpt above is in the text-based Xcode configuration file (.xcconfig) format. You could also just paste the “-Wl,-no_warn_duplicate_libraries” argument into the Xcode build settings for your target, but generally speaking, using Xcode configuration files is better.

Supporting Multiple Xcode Versions

One problem with pasting the OTHER_LDFLAGS value in to Xcode’s build settings, is you won’t be able to build the project on Xcode 14 or earlier. Maybe that is OK, but if like me, you use an older version of Xcode on your build machine (or your dedicated continuous integration service does), you’ll run into a build error when the older linker fails to recognize the new option:

ld: unknown option: -no_warn_duplicate_libraries

So how can we suppress the warnings while using Xcode 15, while continuing to build without errors on older versions of Xcode? Xcode configuration files to the rescue! Because of the general utility of the method used to conditionalize build settings based on Xcode version, I have written a separate blog post describing this technique. Read Conditional Xcode Build Settings to learn more.

Addressing the Underlying Issue

I’m not going to fret too much about disabling the “warn_duplicate_libraries” option on the linker, because I don’t foresee it protecting me from many real-world pitfalls. However, all else being equal I would leave the warning on, because you never know.

I filed FB13229994 with a sample project demonstrating the problem. Hopefully at some point in the future Apple will update Xcode so that it effectively removes duplicated library names from its list of derived dependencies, alleviating the problem and allowing the warning to be enabled again.

Xcode Build Script Sandboxing

Apple added a new build setting to Xcode last year, ENABLE_USER_SCRIPT_SANDBOXING, which controls whether any “Run Script” build phases will be run in a sandbox or not. From the Xcode 14 Release Notes:

You can now enable sandboxing for shell script build phases using the ENABLE_USER_SCRIPT_SANDBOXING build setting. Sandboxing blocks access to files inside the source root of the project as well as the Derived Data directory unless you list those files as inputs or outputs. When enabled, the build fails with a sandbox violation if a script phase attempts to read from or write to an undeclared dependency, preventing incorrect builds.

If I noticed it last year I had already forgotten about it, but I was reminded today while putting together a sample app to demonstrate a bug I was reporting. How was I reminded? Because evidently, starting in Xcode 15, the build setting now defaults to YES. I had added a custom Run Script phase to my project in order to finesse the contents of the built product, but when the script ran I was greeted with this error:

error: Sandbox: cp(25322) deny(1) file-read-data /Users/daniel/Project/File.txt

Luckily when I searched the build settings for the word “sandbox” it turned up the setting, and I was able to turn it off. If you run into this with your projects, it sounds like a better fix is to specify the specific input and output files so that the script phase is allowed access only to the files you think it should be working with.

Inline AppleScript Documentation

While perusing Xcode’s AppleScript scripting dictionary, I was surprised to discover a rather robust “example code” section included right there among the usually spartan reference of the app’s scriptable entities:

Screenshot of Xcode's scripting dictionary, including a section with example script code for the 'build' command

Curious to learn more, I used a relatively little-known trick for examining the raw source code of a scripting dictionary. Simply click and drag from Script Editor’s document proxy icon, into a text editor such as TextEdit, Xcode, or BBEdit:

Screenshot of Script Editor window for a scripting dictionary, with annotations to show where the document proxy icon is

This trick is especially handy because even if the app in question doesn’t have a standard scripting definition (.sdef) file representing its interface, Script Editor will generate one dynamically for you. It’s a quick-and-dirty way to learn how specific outcomes are achieved, and how you might incorporate similar features in your own app’s scripting definition file. In this case, I discovered a new (to me) “documentation” element in the file:

Screenshot of source code reflecting the example script inline documentation pictured above

I was not aware of the feature until a few weeks ago, but apparently it’s been there since at least Mac OS 10.5. Script Debugger supports it, too! You can read more about the “documentation” element by invoking “man 5 sdef” from the Terminal:

When an element needs more exposition than a simple ‘description’ attribute can provide, use a documentation element. A documentation element may contain any number of html elements, which contain text that will be displayed at that point in the dictionary.

Given the sad state of support for AppleScript by Apple, and the continued low level of adoption by 3rd-party developers, a feature like this will probably never be widely used or celebrated. But at least in FastScripts 3.2.4, which I just released this morning, I’m now taking advantage of it:

Screenshot showing FastScripts 'search text' command with an inline documentation block

If you’re among the few remaining Mac developers who is investing time and effort into your AppleScript scripting definitions, hopefully you’ll find this feature useful!

Quieting CVDisplayLink Logging

In recent months I’ve noticed an accumulation of garbage log messages when I’m debugging an app in Xcode. Line after line along the lines of:

[] [0x124858a00] CVDisplayLinkStart
[] [0x124858a20] CVDisplayLink::start
[] [0x600003c3cee0] CVXTime::reset

Today I went digging to find the source of these log messages, and in the process I discovered a workaround that disables them. Just pass “-cv_note 0” as a parameter to the Run task in the Xcode scheme. Alternatively, you could disable them across all of your apps by setting this global default:

defaults write com.apple.corevideo cv_note 0

The only downside to disabling the messages globally is that you will have to remember you disabled it if you ever decide you want to see massive quantities of Core Video debugging logs!

I discovered this user default by spelunking the system frameworks code from which they originate. After setting a breakpoint early in my app’s launch, I set a breakpoint on a variety of logging related methods until I discovered that these messages are all originating from a function called cv_note() in the CoreVideo framework. Within that function, there is a call to another function called “cv_note_init_logging_once”, and within that is a check for a user default value along with this tasty morsel:

Screenshot of Xcode disassembly showing a log message: Thanks for setting defaults write com.apple.corevideo cv_note -int %d

And thank you, Apple, for providing such a mechanism, even if it’s unfortunately configured to log by default. I filed FB9960090: “CoreVideo logging should default to 0”, requesting that Apple change the behavior of this initialization function so that if a user has not set the cv_note value to anything particular, it defaults to 0.

Hold It Right There

In Apple’s latest round of pro laptops, the much-maligned Touch Bar is notably absent from the lineup. Like many people, I never found the Touch Bar to be super useful, but there was one convenience I consistently found indispensible: its support for invoking Xcode commands while debugging an app.

Any Mac or iOS developer will be familiar with the strip of buttons that facilitate various debugging tasks in Xcode:

screen capture of Xcode's debugger buttons

Being able to enable and disable breakpoints, pause and resume the app, and break into Xcode’s View Debugger are crucial to testing and debugging app functionality, and most of the time just clicking the button right in Xcode is all you need. But in some circumstances, to achieve the desired outcome, one of these buttons would have to be effectively clicked without interrupting an action in the target app such as tracking a button or menu item. In those situations, it was particularly useful to be able to set up the debug scenario in the host app, and then to simply tap a corresponding button on the Touch Bar to invoke the desired debugging utility.

For example if I am debugging FastScripts, my menu-bar based scripting utility, I might want to break into the view debugger while the menu bar icon is highlighted and the mouse is tracking menu items:

screenshot of FastScripts menu bar icon invoked with the script menu displayed

Since I gave up the Touch Bar, I’ve been at a loss for how to deal with this kind of situation, so I’ve worked around the problem as best as I can. Still, it’s frustrating after all that time enjoying the advantages of the Touch Bar, to have to give it up.

Today I finally devised a permanent solution that works independently of the Touch Bar, facilitated by none other than FastScripts itself: custom scripts to trigger the pertinent buttons by way of AppleScript GUI Scripting.

screen capture of FastScripts menu items for View Debugger, Pause or Continue, and Toggle Breakpoints

I chose the keyboard shortcuts to be convenient enough for me to invoke with my left hand while tracking the mouse with my right. Now when I am midway through a menu selection and want to instantly capture the view hierarchy for debugging, I just press Cmd-Shift-Ctrl-` and I’m off to the races.

If you think you might find this useful, download all three scripts and configure them with your favorite keyboard shortcut utility. As I’ve mentioned, FastScripts will do a great job, but the scripts should work just as well with a variety of other utilities.

I want to acknowledge Daniel Kennett, who accomplished the same type of functionality by programming his Stream Deck to simulate the behavior of the Touch Bar’s Xcode interface. His solution inspired me to finally follow through on my longstanding plan to develop these scripts. For those of you who prefer a physical interface, and have access to a Stream Deck, his project might be just the ticket!

Xcode’s Environmental Pollution

For a while now my build server has been issuing a large number of mysterious but important sounding warnings along these lines:

warning: include location '/usr/local/include' is unsafe for cross-compilation [-Wpoison-system-directories]

I’m allergic to warnings, and tend to abide by a 100% no-warnings-allowed policy, at least when it comes to release builds. So it was important to me to track this down and finally silence it.

I was really scratching my head because I don’t specify this include location anywhere in my build commands. Normally when an issue like this comes up, it’s easy enough to debug the problem by copying the build command out of the build log and pasting it into the Terminal. Invoking it independently of the xcodebuild process usually reproduces the same warning, and gives you the specific combination of command line options that is leading to the warning being generated.

In this case however, copying and pasting the command line to the Terminal did not yield the warning. What’s going on?

I figured I would take a step back and just try to reproduce the problem independently from my build scripts. Instead of copying and pasting the specific “clang” invocation that yields the warning, I’ll copy and paste the “xcodebuild” line instead. Bzzt! Still no warning.

After a lot of trial and error, I came across the strangest observation: if I invoke “xcodebuild” from within my Python-based build script, the warning is emitted. If I invoke it directly from the Terminal, it isn’t. In fact, if I simplify my build script to simply invoking “xcodebuild”, the warning happens. Stranger still? If I change the script from “python3” to just “python”, the warning goes away again.

I strongly suspected that the difference in behavior must be based in environment variables, so I decided to add a line to the top of the python script:

import os; print(os.environ)

Sure enough, the environment variables differed when I ran the script with “python” vs. “python3”. To get a feel on your own Mac for how the two commands differ, you could run this as a one-liner with python and with python3:

python3 -c "import os; print(os.environ)"

If your configuration is like mine, the output will include a “CPATH” value something like this:

{... 'CPATH': '/usr/local/include', 'LIBRARY_PATH': '/usr/local/lib'}

That “CPATH” entry for example only exists when invoking the script with python3, and it’s this very environment variable that is creating the unexpected Xcode warnings!

I was perplexed about how or why the version of Python could impact these environment variables, but then I remembered that python3 is bundled in Xcode itself, and the version at /usr/bin/python3 is a special kind of shim binary that directs Apple to locate and run the Xcode-bundled version of the tool. Apparently, a side-effect of this mechanism causes the problematic environment variable to be set! Running the python3 implementation directly where it lives in the Xcode installation:

`xcrun -f python3` -c "import os; print(os.environ);"

Does not exhibit the problematic environment variable!

So the issue is not about running python vs. python3 per se, but about invoking an Xcode build via any mechanism that leads to Apple’s “relocation” shim being executed and inadvertently mucking with environment variables that will impact the build process. I’ve filed FB9776086 requesting that the unwanted environment variables not be set when invoking commands, but in lieu of a system-wide fix, I’ll be adding this to the top of my Python build scripts:

import os
if "CPATH" in os.environ: os.environ.pop("CPATH")

Now my automated builds are beautifully warning-free again!

Dangerous Logging in Swift

I recently came across a perplexing crash in my unit tests that led me down a path of discovery, culminating in an awareness that “I was holding it wrong” when it comes to using NSLog in Swift.

Here is the source code to an extension on FileManager intended to make it easy to read the last modification date of a file:

@objc(rsLastModificationDateAtURL:)
func lastModifedDate(at url: URL) -> Date? {
    do {
        let resourceValues = try url.resourceValues(forKeys: [.contentModificationDateKey])
        return resourceValues.contentModificationDate
    } catch {
        NSLog("Failed to get modification date at URL: \(url) - \(error)")
        return nil
    }
}

I’ve highlighted the line of interest, an invocation of NSLog in the case where a modification date could not be read because of some exception. For example, if the file were not found this line of code would be reached. Looks innocent enough, right? Here’s what I came up against while running the unit tests for one of my apps:

highlighted source code line showing NSLog invocation crashing with EXC_BAD_ACCESS

At first I thought that one of the url or the error variables must be bad. After all, Swift string interpolation and NSLog are both battle-tested. Surely if there were a way to easily crash by logging good variables, it would be fixed a long time ago. But no, both of the variables in this instance were perfectly valid references. So what gives?

The Problem

The problem is rooted in the fact that NSLog from Swift calls through to the underlying C-based interface which supports template strings and variadic arguments. This is why, for example, you can invoke NSLog(@"Hi %@", name) in Objective-C and, if name represents the string “Daniel”, you get “Hi Daniel” printed to the console.

In the scenario of my crash, the interpolation of “url” and “error” results in surprise template placeholders, because of the presence of spaces in the path that the URL represents. In my tests, a file called “Open Program Ideas File” exists and is used to test some file manipulations. When the path to this file is encoded as a URL, the name of the file becomes “Open%20Program%20Ideas%20File”. Since the error’s failure message reiterates the name of the file it couldn’t find, we end up with another copy of the percent-escaped string in the parameter to NSLog. Each instance of “%20” followed by a letter is liable to be interpreted by NSLog as a printf-style format specifier. So as far as our filename’s string is concerned, we have “%20P”, “%20I”, and “%20F” threatening to cause trouble. Capital letters in printf formats are pretty uncommon, but the %F format specifier is documented as:

64-bit floating-point number (double), printed in decimal notation

In short, we have a situation where logging the URL and the error inadvertently asks NSLog to look for a 64-bit floating point number in the arguments list. A 64-bit floating point number of size 20, whatever that means in this case. So, whether the unit test, or app, crashes or not in a situation like this depends entirely on what data happen to be in the places where the variadic arguments would be, and whether that data causes a crash when attempting to interpret it as a type of the specified print format.

How to Fix It

So how do we fix it? Well, we need to make sure that the string that ultimately gets passed to NSLog doesn’t contain these surprise placeholder values. If this were Objective-C, we wouldn’t run into the problem because the parameters would need to be passed as variadic arguments:

NSLog(@"Failed to get modification date: %@ - %@", url, error);

But if we try that in Swift, we run into trouble. NSLog in Swift doesn’t support variadic arguments:

screenshot of build error indicating that variadic arguments are not allowed with NSLog in Swift

I’m pretty sure this is how I ended up in this situation in the first place: I probably tried to adapt my old Objective-C code to Swift, ran into this error, and obediently changed to using inline variable substitution instead. It just seemed like “the Swift thing to do.” But if we can’t use NSLog with inline substitution, and we can’t use variadic arguments, what are we supposed to do? Update: Thanks to Sebastian Celis for pointing out on Twitter that variadic arguments do work with NSLog. I think the failure above is specifically because I’m passing non-NSObjects to NSLog, which isn’t supported.

A few years ago Apple introduced a new logging framework called the Unified Logging System, which supports logging with variadic arguments from both Swift and Objective-C. It also supports a host of other features that allow you to categorize and prioritize your log message and, well, it sounds great, but it’s also quite a bit more complicated than just invoking NSLog. It’s complicated enough that I can’t offer a one-line fix for the situation I’ve described here, but I encourage you to investigate your options.

Audit Your Code

I also encourage you to audit your own code, and I’ll describe a method for searching your own code base to see if you might be vulnerable to the kind of unpredictable crash that inline interpolation can cause with NSLog. In Xcode, create a custom search scope that allows you to easily search just the Swift files in your code base, then search for instances of the problematic pattern. Obviously if you’ve got a 100%-Swift code base, creating the custom search scope is not necessary:

  1. Show the Find Navigator by selecting View -> Navigator -> Find from the menu bar.
  2. Click the “In Project” (by default) search scope label beneath the search field. This reveals the list of search scopes currently configured in your workspace.
  3. Click the “New Scope” button and configure a scope designed to match all files with the “swift” file extension”:

    screenshot of custom search scope popover showing title

  4. Click the Find type (“Text” by default) in the search parameters above the search field, and change it to “Regular Expression”.
  5. Type or paste in the search string NSLog\(.*\\\( — this is a regular expression that will find intances of the string “NSLog(” followed by any number of characters that includes the substring \(, the tell-tale sign of Swift string interpolation:

    screenshot of search results showing multiple files with

In my own code base, this audit results in an alarming 23 instances in 17 files, all of which I will soon be converting to a safer logging method. I encourage you to do the same in your code base. Hopefully you’ll have been followingn a safer pattern all along and won’t have any work to do, but if you’re anything like me, you might find you’re more vulnerable to the problem than you thought!

Not Applicable

From time to time, particularly when I’m working with xib files of a certain vintage, Xcode simply refuses to offer any inspector tools for the contents of a particular Interface Builder document. Instead of the typical controls for setting an object’s properties, I get this:

UntitledImage

Look familiar? It’s super-frustrating when you simply can’t get at the settings for something you desperately need to change. I’ve sometimes gotten Xcode to relent through some combination of closing the file in question, closing assistant editors, etc. But sometimes I just can’t get it to show the properties for an object no matter what I try.

The one thing that always works is to simply make a copy of the xib file in question, which Xcode will happily edit, and then copy it back over the original. It’s really that simple:

  1. Copy MyInterface.xib to MyInterface Copy.xib
  2. Double-click the copy to edit it.
  3. Save changes.
  4. Replace MyInterface.xib with MyInterface Copy.xib

This has vexed me long and hard enough that I thought some others will not have recognized there is such an easy workaround. It would be great if Apple fixes whatever bug is causing this, but in the meantime at least there’s a solution.

Similar Detritus Not Allowed

Over the past few weeks, I’ve noticed a spike in the number of Mac and iOS developers who are running into a specific code signing error while building their apps. I myself ran into it last week after saving a new version of an image file from Preview into my app’s bundled resources.

I’ve noticed folks on Twitter and in developer Slack’s coming up with the same problem. I don’t know if something has changed in the code signing toolchain, or we’re just having an unlucky break, but I thought I’d blog about it because it seems many people may need this advice now.

The error in question is always along these lines:

resource fork, Finder information, or similar detritus not allowed

It comes up when the code signing process comes across traces of metadata in one of the files that is bundled with your app, or in some instances in the binary executable itself. This problem is common enough that Apple even posted a technical note several years ago that explains:

Code signing no longer allows any file in an app bundle to have an extended attribute containing a resource fork or Finder info.

This technical note gives advice for how to use the xattr tool to both examine and remove the unwanted extended attributes on the file. At least one colleague has reported that whatever was wrong with the file in their case was not detectable by xattr and therefore also not reparable with that tool.

Here’s a foolproof trick that is likely to address the problem, whether it’s unwanted extended attributes, resource forks, or whatever. Simply cat the affected file into another file, and then replace the original. For example, if you’ve got an image file called Whatever.png in your app, and it’s triggering the error above, try this in the Terminal:

cat Whatever.png > Whatever2.png
mv Whatever2.png Whatever.png

Et voilà! It’s as simple as that. The cat command cares not about whatever special attributes or resource forks are causing the code signing process grief. It only cares about the binary bits that comprise “the main file”.