Delivering Unity macOS build to Steam and AppStore
This blog records how to setup and config the application bundle which will be submitted to the AppStore or Steam by using the CLI tools. Mostly, we use XCode for developing iOS and macOS applications, which automatically handles the code signing and configurations. But when exporting an app bundle from Unity Editor, we have to do this work by using command line tools that are provided by Apple.
In the begining, let’s get some preliminary knowledege about the CodeSign
and Distributions
. Or you can just skip this part then jump into the process parts.
Submit to Steam store or third-party distribution
Submit to macOS AppStore
Bundle Config and Code Signing
Basic Concepts
Apple provides code signing tool which is called codesign
. In general, the execuatable application in macOS system presents as a folder that has name with .app
suffix. It’s different from windows. So for signing the app become more complicated, because we are facing a bunch of files not just a single .exe
file.
Designated Requirement(DR)
Every application requires a Designated Requirement(DR) when code signing. The DR is identified by the bundle’s BundleID.
Resource envelope
CodeSign tool will process all sourcefile and non-sourcefile. Every file calculates a hash string, then combined these hashes into a final hash value which will be attched to the application bundle.
Any modification apply to the signed files will change the hash that belonging to this files, and obviously affects final hash. By using this method, other security programs can verify the identity and integrality.
The concept for files signature data is call Resource envelope
.
Resource envelope has two versions, the latest v2 version will check all the files inside the bundle. After OSX Mavericks 10.9, all app use the v2 version.
Even if we didn’t touch the bundle file, any extra file added to the Game.app
bundle folder, will casue the bundle codesign verification failed.
Process application bundle
Modifiy the BundleId
Directly open the Plist file Game.app/Contents/Info.plist
change the value of key CFBundleIdentifier
。
Remove useless files
Unity Edtiro always genereate .meta
files for all the imported macOS library. These .meta file were placed at illegal path for application bundle. And causing the codesign process failed. We must delete them.
1 | #remove all remove meta files |
CodeSigning
Signing Third-party libs
Apple requires all the libraries must be signed before submiting to AppStore or Notarization.
All libraries include .dylib
.framework
and .bundle
.
When submiting to AppStore, the third-party libs must be signed by current Apple Developer
, even if them were codesigned or not. Which means that we also need to codesign the common shared libs such as libssl.dylib
.
Entitlements Files
Before use codesign tool, we need to create .entitlements
file. Xcode usually create them for us, but now we are using CLI tools and tackling with exported binary file, don’t we? The entitlements file which used for AppStore submission differ from the Steam one.
The minimal configurations are provided below, other key/value pairs must be added if there have other capabilities.
For macOS AppStore
1
2
3
4
5
6
7
8
9
10
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist
For Steam
1 |
|
CodeSign Tool
codesign command for signing a single file
1 | codesign --deep --force --verify --verbose=4 --timestamp --options runtime --entitlements $EntitlementsFile --sign "$CertName" $FileName |
Parameter sign should be filled with the name of certificate. Like Mac Developer:XXXXX(TEAMID)
. Codesign tool will search all the certs in user’s keychain, find the correct one. Throwing error when found multiple certs match the inputs.
Command for verification the codesign status.
1 | codesign --verify --deep --strict --verbose=4 Game.app |
Success with these outputs.
1 | Game.app: valid on disk |
Submit to SteamStore and non AppStore distribution
The binary app which submitted to SteamStore must be notarized by Apple, otherwise the program will be blocked by GateKeeper when user try to launch the app.
The specified notarizing docs from Apple Notarizing macOS Software Before Distribution
Generate Certificate
First step is creating a certificate. Signin to the Apple Developer website then generate a Developer ID Application
cert.
Only the account holder of the developer team can create this Developer ID Application Certificate
,even if the user have admin permission role. Just like any other developer cert, export a CSR file from keychain tool, then upload for swapping a .cert file.
Bundle preparation
Now we have a Game.app
binary bundle, and a Developer ID Application Certificate
. But before signing, bundle needs to be preprocessed.
Following the instruction of first part Process-application-bundle.
- delete all
.meta
file recursivly inside theGame.app
folder. - Create
Game.entitlements
file as informed. - Modify
Game.app/Contents/Info.plist
if needed.
Fix SteamLib
Most of developer use third-party Steamworks Librlabrecque/Steamworks.NET with Unity, because the VALVE didn’t provide the C# version SDK。Before codesign we needs some patches。
The lib file of rlabrecque/Steamworks.NET
is Steam_api.bundle
.
- Steam_api.bundle contains an invalid CFBundleIdentifier:
com.rileylabrecque.steam_api
. Change it tocom.rileylabrecque.steamapi
in the filesteam_api.bundle/Contents/Info.plist
。 libsteam_api.dylib
contains 32bit symbols. We only use 64bit symbols, delete it with commandlipo -remove x86_64 libsteam_api.dylib -o libsteam_api.dylib
.
No we have successfully processing the Steamwork library. These rules are also demanded with the other libraries. You should apply the patches if there exist.
CodeSigning
The instruction of apple developer doc recommands that codesign all libs before signing the final bundle. Using --deep
parameter does not guarantee the correctness with nested libraries. It’s just for codesign patching after file modification.
I just write a simple bash script for codesign command execution.
- Sign all libraries
$BINARY = ‘Game.app’
1 | libfile=`find ./$BINARY -name '*.dylib'` |
- CodeSign final application bundle
1 | codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements $ENTITLEMENTS --sign "$SIGNCERT" $BINARY -i $BUNDLE_ID |
Emphasis: --sign
parameter will automatically match, just input “Developer ID Application:”.
Submit to Notarization
- Zip archive the bundle before notarization
Warning: We should use the command ditto
for archive the budnle, use zip
cause the missing of signature infomation
1 | ditto -c -k --sequesterRsrc --keepParent "Game.app" "Game.app.zip" |
2.Submission
Now we have the ziped bundle. It’s time for notarization.
1 | xcrun altool --notarize-app --primary-bundle-id "${Bundle_Identifier}" --username "${APPLE_ID_ACCOUNT}" --password "${APPLE_ID_PWD}" --file Game.app.zip --asc-provider "${ASC_PROVIDER}" |
Parameter asc-provider
requires ProviderShortname
.
It’s commonly equals Team ID
which can be found at
‘Apple Developer/Membership/Team ID’.
But unfortunately, my apple developer account is pretty old. These are not equal.
Don’t worry, apple provide the tools for quering the ProviderShortName
.
1 | $ xcrun altool --list-providers -u "AC_USERNAME" -p "PWD" |
If everything gose well, we may get a RequestId
like ‘1809fca2-XXXX-XXXX-XXXX-14aa62f6XXXX’.
1 | altool[16765:378423] No errors uploading 'Game.app.zip'. |
With this ReqId, we can query the result of notarization. It needs about 5mins for apple server processing the notarization. Maybe longer depends on the file size.
1 | #查询结果 |
When processing finish, Apple will send an email to your developer ID.
If the notarization failed, all erros can be find by check the log file.
- Staple App bundle after notarization
1 | $ xcrun stapler staple "Game.app" |
Please keep in mind that we can’t staple with Game.app.zip
file.
Staple only apple to app bundle. When staples successfully, zip the Game.app
again, then we can submit it to the user or SteamStore.
Submit to macOS App Store
Certificate Creation
Two different certificate are required when submit to macOS AppStore
- Mac App Distribution Certificate
- Mac Installer Distribution Certificate
Mac App Distribution Certificate
is used for bundle CodeSign
.
Mac Installer Distribution Certificate
is used for pack bundle then uploading to itunes server for review.
Bundle Configuration
Same as Submit-to-SteamStore-and-non-AppStore-distribution
- delete all
.meta
file recursivly inside theGame.app
folder. - Create
Game.entitlements
file as informed. - Modify
Game.app/Contents/Info.plist
if needed. - Process third-party libs
- CodeSign all libs. (use
Mac App Distribution
) - CodeSign Game.app. (use
Mac App Distribution
)
Generate .pkg file
After codesign, we need to pack Game.app
into Game.app.pkg
.
Use productbuild
tool
1 | productbuild --component ${BINARY} /Applications --sign "3rd Party Mac Developer Installer:" "${PKG_FILE}" |
Use AppLoader for uploading
Now we have Game.app.pkg
bundle which is permitted for uploading.
It same process like iOS developement. use application loader tool for uploading to itunes server. Due to the removal of GUI version ApplcaitionLoader with Xcode11,
we can only submit with command line use xcrun altool
.
- Verification before submission
1 | xcrun altool --validate-app -f Game.app.pkg -u "${USER}" -p "${PWD}" |
Tips: xcrun altool --validate-app
will not found all the erros, because apple itunes server will do the more stricter validation.
- Uploading
1 | $ xcrun altool --upload-app -f Game.app.pkg -u "${USER}" -p "${PWD}" |
If get some output like no error uploading package
,means upload successfully.
Apple will also send an email when validation failed. Like this:App Store Connect: Your app "XXXXX" (Apple ID: 123456789 Version: 1.0 Build: 0) has one or more issues
But if the validation got through, no email sent. Remember to check the itunes connect dashboard.
Fix all the errors that apple email mentions. After solve the errors, the process steps of codesign->pkg->upload must be executed completely. Do not forget to increse the build version number.
Every uploading task requires different build version number.
FAQ
I just list some common errors.
- ITMS-90303: Unable to Sign
code object is not signed at all In subcomponent: PATH_TO_THE_FILE.
Delete these files before codesign.
- ITMS-90238: Invalid Signature
Some libs are not signed, or signed with incorrect cert.
- codesign throw error: CSSMERR_TP_CERT_REVOKED
The certificate is out-of-date, download the new one.
- codesign 提示 errSecInternalComponent
If you do the codesign
or productbuild
via ssh, you should unlock the keychain with CLI. Because some process require user password to unlock the keychain. There is no password dialog pop up when these program request an access to the keychain.
Reference
Unity Manual: Delivering your application to the Mac App Store
gist:how-to-notarize-unity-for-macos.md
Notarizing macOS Software Before Distribution
Technical Note TN2206:macOS Code Signing In Depth
Thanks for reading, If there are any mistake or question, feel free to leave a comment below.