SwiftPM binaryTarget dependency and code signing

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