Embedded Content Contains Swift
By: . Published: . Categories: gotcha swift.If you’re developing a QuickLook plugin using Swift,
make sure you flip on the EMBEDDED_CONTENT_CONTAINS_SWIFT
build setting for
the target, otherwise bundle loading will fail in a spectacularly unhelpful
way.
Creating a Mixed-Language QuickLook Plugin
Recently I decided to add a QuickLook plugin to my ImageSlicer utility app.
The default QuickLook plugin template stamps out an entirely C plugin.
Changing the thumbnail/preview template files to have a .m
suffix
put us back in Obj-C land, but getting to Swift land takes a couple more steps.
Not to worry: Add a new Swift file to the target, and Xcode will offer to make bridging easy-peasy for you. Give it the go-ahead, and you should be good to go, right?
I add the main model and view classes from my app project to the QuickLook target, wire stuff up to load the document and render the view, and everything compiles and links all happy-like. Let’s test this thing!
Gatekeeper?
I fire up qlmanage, point it at my generator and a .slicedimage
document,
and I see That Error:
The bundle “QuickLookSlicedImage” couldn’t be loaded because it is damaged or missing necessary resources.
I’ve seen this error way too many times when I grab an older app bundle off the Internet. Every time before, “damaged or missing necessary resources” has been code for “no-one signed this app bundle”.
I’m asking the system to execute code, so, sure, that kind of makes sense?
I hare off looking at using spctl
to whitelist my bundle,
successfully whitelist it with
spctl --add --label JWSDev path/to/QuickLookSlicedImage.qlgenerator
,
and spctl --assess
is OK with it.
Let’s try again.
Not Gatekeeper
I see the same error. Hrm. What if it really is missing something? Now I want to see the smoking gun.
After sufficient rooting around, I eventually work through to where it loads
the bundle, then the plugin, then finally to where the real business happens:
dlopen
.
After the call to dlopen
, the CFBundle
machinery checked for success with
dlerror
, and that gave me an actually informative error message
(which I’ve abbreviated and hard-wrapped for readability):
(lldb) x/s $rax
0x100576819: "dlopen(LONG_PATH/QuickLookSlicedImage, 262):
Library not loaded: @rpath/libswiftAppKit.dylib\n
Referenced from: LONG_PATH/QuickLookSlicedImage\n
Reason: image not found"
Yup, missing Swift dylibs.
EMBEDDED_CONTENT_CONTAINS_SWIFT
The fix is to tell Xcode to copy all the Swift dylibs the built product needs
into its bundle using the build setting EMBEDDED_CONTENT_CONTAINS_SWIFT=YES
.
(The other fix is to ensure qlmanage
is actually running the generator
you’re building now, not the generator embedded in the copy of your app
you built an hour or two ago that still has the missing-dylib issue.
Oops.)
Take-Away
The take-away is this:
- When Xcode offers to add a Swift–Obj-C bridging header for you,
- Then that means the target was not previously configured for Swift,
- And you should probably ensure that
EMBEDDED_CONTENT_CONTAINS_SWIFT=YES
gets set for the target.
The “probably” is there because, if you’re baking it into an app bundle that’s already embedding the Swift dylibs, you could probably mess with the rpath to get it to share those rather than having Yet Another Copy of the Swift support dylibs in your app bundle.
But that’ll be a pain, and disk space is cheap, so you’ll probably still want
to just flip on EMBEDDED_CONTENT_CONTAINS_SWIFT=YES
.