App-store signing is a tedious process and when it is done without Xcode, so many things could go wrong. This guide provides a detailed step-by-step explanation on how to re-sign an iOS/tvOS archive without Xcode. This can be used when you have xcarchive file that needs to be converted to an IPA and then signed with different profiles and certificate. This also describes how to change bundle ID of the application.
General folder structure
- The .app file can be found within the Products folder in the .xcarchive file
- Frameworks folder can be found within the .app file.
- All the extensions to the app can be found within Plugins folder within the .app file
Creating IPA file and Entitlements file
- First, extract the entitlements from the existing archive file. These later need to be used to sign the IPA file. Run the following command to extract the entitlements into a .plist file. Do this for the main app and any plugins/extensions within the project.
codesign -d --entitlements :- OUTPUT_FOLDER/Payload/APP_NAME.app > APP_NAME_ENTITLEMENT.plist codesign -d --entitlements :- OUTPUT_FOLDER/Payload/APP_NAME.app/PlugIns/EXTENSION_NAME.appex > EXTENSION_NAME_ENTITLEMENT.plist
- Next, create an IPA file from the xcarchive file provided. The reason is that xcarchive files cannot be signed, only IPA files can be signed. Retrieve the .app file from the Products folder and place this in a new folder called “Payload”. Also retrieve the ”SwiftSupport” folder from the xcarchive and place it at the same level as Payload folder.
If the SwiftSupport folder is missing, apple rejects the build during the verification process while uploading on to app store connect. Now, compress these two folders together to create a new zip file. Rename the zip to any name and change the extension to .ipa. You now have your IPA file.
Removing existing signature
- Now, unzip the IPA file to remove existing signature and re-sign this. Run the following command to unzip the IPA file.
unzip -q YOUR_IPA.ipa -d /OUTPUT_FOLDER_PATH
- Next remove the existing signatures (if any) from the IPA file created earlier. This step is a bit more involving as we need to remove signatures from frameworks, main app and any app extensions within the IPA.First, run the following command to remove signature from frameworks
rm -r YOUR_APP.app/Frameworks/*/_CodeSignature (should be within the Payload directory)
Then remove signature from .app and all of the .appex (if any). Run the following command to do the above. Make sure to be within the correct directory before running these.
rm -rf _CodeSignature
Replace provisioning profiles
- Next, replace the existing provisioning profiles with the new ones. The new provisioning profiles need to be copied in as the embedded.mobileprovision. This needs to be done for main .app file as well as all the .appex files (if any). Run the following commands.Command to replace provisioning profile for main .app file
cp /PATH_TO_NEW_PROVISIONING_PROFILE_FOR_MAIN_APP /PATH_TO_EXTRACTED_IPA_DIRECTORY/Payload/YOUR_APP.app/embedded.mobileprovision
Command to replace provisioning profile for .appex files (if any)
cp /PATH_TO_NEW_PROVISIONING_PROFILE_APP_EXTENSION /PATH_TO_EXTRACTED_IPA_DIRECTORY/Payload/YOUR_APP.app/PlugIns/YOUR_APP_EXTENSION.appex/embedded.mobileprovision
- (Optional) If the bundle ID needs to be changed, this can be done by opening the info.plist file within the “Payload” folder. Please note that if you are changing the bundle ID of the main app, the same needs to be done for the extensions as well. The info.plist file for extensions can be accessed from within the .appex files.Also, you can change/bump the version number from the info.plist file. However, you need to make sure this is done for the extensions as well.
Codesign all the targets
Now, it’s time to codesign the IPA file.This step is again a bit more involving and needs to be done in the same exact order as described here. This order is important. The internal folders must be signed first.
- First, we need to codesign the frameworks. Run the following command to codesign all the frameworks at once.
codesign -f -s "iPhone Distribution: YOUR_CERTIFICATE_NAME" YOUR_APP.app/Frameworks/* (should be within the Payload folder)
- Next, we need to sign all the app extensions (if any). Run the following command for each extension within the archive.
codesign -f -s "iPhone Distribution: YOUR_CERTIFICATE_NAME" --entitlements /PATH_TO_ENTITLEMENTS_FILE.plist /PATH_TO_EXTRACTED_IPA_DIRECTORY/Payload/YOUR_APP.app/PlugIns/YOUR_APP_EXTENSION.appex
- Finally, the main app needs to be signed. Run the following command to sign the .app file
codesign -f -s "iPhone Distribution: YOUR_CERTIFICATE_NAME" --entitlements /PATH_TO_ENTITLEMENTS_FILE.plist /PATH_TO_EXTRACTED_IPA_DIRECTORY/Payload/YOUR_APP.app
And, we’re done. Now everything needs to be packaged up. Run the following command to zip everything back up.
zip -qr ../YOUR_APP-RESIGNED.ipa .
Note: While performing the code sign process, I did notice that I kept getting the following error:
“Code signing fails with error ‘resource fork, Finder information, or similar detritus not allowed”.
This is a security hardening change that was introduced with iOS 10, macOS Sierra, watchOS 3, and tvOS 10. Code signing no longer allows any file in an app bundle to have an extended attribute containing a resource fork or Finder info.
In order to get around this run the following commands:
xattr -lr /PATH_TO_YOUR_.APP_FILE (To see which files are causing this error)
xattr -cr /PATH_TO_YOUR_.APP_FILE (This removes all extended attributes from your app bundle)