SwiftPM binaryTarget dependency and code signing

How do you make sure the script runs after all other build phases? I've got it dragged all the way to the end of the list in Build Phases. But when I build I can see in the build report that it's running my script, then immediately copying the framework. So the framework isn't there to sign when it gets run.


BuildReport

I've added a "Copy files phase" which copies the frameworks:

It needs to be a Scheme post-action, not a build phase. See screenshot example at

Great, thanks both of you for options.

We ran into the same signing issue and published a workaround that should work for all common setups, iOS and Mac Catalyst. Library Not Found issue When Running on an iOS Device Using Swift Package Manager | PSPDFKit

7 Likes

Same issue is happening for me. I just switched my project from Cocoapods to the 6.33-spm-beta branch using SPM (had to add dependency with the GM, as the process is broken in 12.2). I assume the fix for now would be to switch back to Cocoapods.

Thanks @steipete, this workaround was successful for us.

Workaround works great! Now we need Apple to fix Xcode.

1 Like

There is still an issue if a project uses a Swift package which itself declares a binary Swift package as dependency (Project → Source package → Binary package instead of Project → Binary package), which is the problem described by the message initiating this discussion.

In this specific case Xcode copies the unsigned transitive binary dependencies last, after code signing has been made. The resulting app bundle is therefore incorrectly signed and cannot be run on a device.

Since this framework copy operation occurs last and there is nothing we can do to make it run earlier AFAIK, attempting to sign the binaries with a usual target build phase does not work, as it is run earlier in the build process. We must therefore fix code signing after the build is finished and the app packaged. A build post-action phase thus seems appropriate.

Problem is that the actual code signing identity EXPANDED_CODE_SIGN_IDENTITY is not known to a build post-action. Here is therefore a workaround made of a build phase whose sole purpose is to extract the signing identity to a temporary file, later read by the post-action script which signs all frameworks in the app bundle product:

  1. Select the target you need to fix code signing for, go under Build Phases and add an Extract Signing Identity script phase, with the following content (replace target-name with a proper unique name):

    echo "${EXPANDED_CODE_SIGN_IDENTITY}" > /tmp/target-name-signing-identity
    
  2. Edit the scheme corresponding to your target (which you probably readily have), open the Build section and add a Post-action run script phase, with the following content (use the same target-name as above):

    CODE_SIGN_IDENTITY=`cat /tmp/target-name-signing-identity`
    find "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mindepth 1 -maxdepth 1 -type d -exec /usr/bin/codesign --force --sign ${CODE_SIGN_IDENTITY} --preserve-metadata=
    

Of course I recommend to share the scheme so that your project builds and runs on a device after checkout.

Note that this workaround signs all copied frameworks in the app bundle product and therefore also works for the Project → Binary package case, though it involves a second script phase in comparison to the solution proposed by @steipete.

2 Likes

Xcode is supposed to re-sign all of your libraries and frameworks upon export of the archive, or is that incorrect?

Yes, but if you attempt to run the app on a device directly from Xcode it will fail (which is quite annoying when you have something to debug).

1 Like

Thanks for this! I think part of your script may have gone missing. It worked for me when I used this:

find "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mindepth 1 -maxdepth 1 -type d -exec /usr/bin/codesign --force --sign ${CODE_SIGN_IDENTITY} --preserve-metadata= '{}' \;

You are right, sorry for the bad copy & paste. I actually preserve identifier and entitlements metadata, so the final command looks like:

find "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mindepth 1 -maxdepth 1 -type d -exec /usr/bin/codesign --force --sign ${CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements {} \;
1 Like

Was this issue resolved in the latest Xcode 12.2 beta?

The issue has been fixed in Xcode 12.2 beta 3. The binary frameworks are now properly code signed, as can be seen in the build log.

Note that you have to switch your command-line tools to this specific beta version (12B5035g) and to cleanup your build folder first.

A big thanks to the people who fixed this issue!

Remark: The fix has not been backported to 12.1 GM, the workaround is still required for this version.

8 Likes

Xcode 12.3 does fix the issue for me, but it looks like I'm still running into another issue specific to static frameworks: Xcode copies (and signs) the static framework in the app's Frameworks directory.
The framework is properly statically linked to the app, so it runs but it unnecessarily heavier than it should.

2 Likes

Yea, moving static archives (.a) into the Frameworks directory is still a big problem!

Embedding static frameworks also causes the application package to be invalid when uploading to the App Store.

1 Like

Unfortunately this is still an issue with Xcode 12.2 RC

Yep, we're still seeing the issues with static frameworks in Xcode 12.2 RC requiring complicated workarounds. This is a major usability issue impacting migration to Swift Package Manager.

Can anyone provide an update on a fix?

1 Like