Signing and Uploading apps to the Mac App Store
This article is a checklist of all the tasks I ran into that were required to get a LiveCode macOS app onto the AppStore, last used February 20, 2019. Because the AppStore requirements change continually, this checklist will slowly get out of date, experience bit-rot. Let me know if these instructions fail you and I'll attempt to fix them. My thanks to the LiveCode community and LiveCode support team. Much of this arcane voodoo was passed on to me by others.
Please note, line wrapping within this article for the command line commands can hide or imply spaces. Suggest you copy the command lines into a text editor to see the correct command, spaces or not. Kee Nethery
1. Application Icon / Document Icon
The application icon is required to be available in a set of sizes. Instead of an asset catalog, LiveCode uses an .icns file for the app icon images. I found two ways to generate the .icns file and both require a 1024x1024 png app icon image.
GraphicConverter 10: Open the png and Save As the file type .icns.
Asset Catalog Builder: Is available on the App Store. Creates an asset catalog of all the required icon resolutions but it does not put them into an .icns file. Take the output folder from asset catalog builder, remove the json file, rename the folder "<whatever>.iconset". Replace <whatever> with any word you desire. The important part is that the name end in ".iconset". In Terminal enter:
iconutil --convert icns <path_to_iconset_folder>
iconutil --convert icns "/Users/myname/SDK Mac Apps/MyApp/grafica/icon.iconset"
Note, if you are not familiar with Terminal, it is in Applications / Utilities and instead of typing the path to the iconset folder, you can drag and drop that folder into Terminal and it will enter the complete path for you.
Select the resulting .icns files in your Standalone Application Settings...
To verify your .icns file has a 1024x1024 image, open it in Preview and "Get Info" on the images to see if one is 1024x1024. The first image in my .icns file is that resolution.
2. Prepare your stack for building
Whatever automated script you have that cleans up your stack before you turn it into an application, run that now.
3. Set the version number
Each upload to the App Store needs a unique version number. In LiveCode Application Settings / Mac / Short Version, increment the version number. In my case I submitted 2.0 and that version was rejected so I changed the next version to be reviewed to 2.0.1. The App Store uses the version values in the Standalone Application Settings windows so be sure to provide that exact version to the App Store.
Note, the version number (CFBundleVersion and CFBundleShortVersionString) can be at most three positive integers separated by periods. LiveCode defaults to 126.96.36.199 which violates that rule. A version number of the format 1.0.0 is acceptable.
Note also that each version that gets uploaded needs a new version number. When you upload a new version and Apple comes back with an error email telling you about something that needs to be fixed, you will need to increment your version number before uploading the corrected application.
4. Build the standalone
Save as standalone application. Build for Mac OS X 64-bit only. Apps with and 32-bit code in them will be rejected when uploading.
5. Remove extended attributes
LiveCode saves a bunch of extended attributes into the files in the standalone application and they must all be removed. Use the Terminal command line to see all the stuff that needs to be removed:
sudo xattr -lr <path_to_standalone_app_bundle>
And to actually remove all that cruft:
sudo xattr -cr <path_to_standalone_app_bundle>
Run the first command line "-lr" a second time to verify that all the cruft has been removed.
6. Rename localization files
The App Store looks at the list of localization .lproj files to determine which languages the app supports. Apple is no longer using the names of languages (English, Dutch, Japanese, etc), instead the lproj folders must use the proper ISO codes. For example en.lproj is for English, jp.lproj is for Japanese, etc.
Open the standalone app to Show Package Contents / Contents / Resources and remove all the lproj folders that your app does not support. Rename the others using the ISO codes. My app only supports English so the only lproj folder in my app is en.lproj.
7. Check the standalone icon
In that resources folder should be the icns file you created. It will be renamed to "Standalone.icns". If not, you didn't select your icns file in the Livecode / Standalone Application Settings / Mac OS window.
8. Add an encryption key to the plist
If your app uses no encryption other than HTTPS (yes HTTPS is encryption, it is OK to use), add into the plist the flag that tells Apple "I'm not using encryption other than HTTPS". Go to Show Package Contents / Contents / Info.plist and add in:
Whatever the file format of the plist, don't change it when you make this edit. I use BBEdit for these kinds of file edits. It handles file write permissions and such.
9. Add a category type key to the plist
10. Add a minimum system version key to the plist
In Info.plist one must add a value for LSMinimumSystemVersion. I am using LiveCode Indy 9.0.2 and according to Wikipedia, LiveCode 9.x supports 64-bit Intel macOS 10.9.x and greater.
For my app, I added:
11. Set the tsNet bundler identifier
NOTE: tsNet.bundle is only included in apps that utilize internet connectivity. Skip this step if you do not have "tsNet.bundle" in Show Package Contents / Contents / MacOS / Externals / tsNet.bundle.
There is a code resource within LiveCode app bundles named tsNet and your tsNet in your app needs to be "owned" by you otherwise you get a CFBundleIdentifier Collision when uploading your app. Go to Show Package Contents / Contents / MacOS / Externals / tsNet.bundle / Show Package Contents / Contents / Info.plist If your domain is "YOURCOMPANY.com" and your app is named "YOURPRODUCT" then replace "au.com.techstrategies.external.tsNet" with "com.YOURCOMPANY.YOURPRODUCT.tsNet" in the Info.plist file in the tsNet.bundle.
For my app, I edited it to:
12. Remove 32-bit code from revsecurity.dylib
Note: not all apps will have revsecurity.dylib
Note: LiveCode 9.0.1 and earlier are outputting a universal version (64-bit and 32-bit) of revsecurity.dylib. Future versions might cease doing that. The number of bytes in the disallowed version is 3,892,216 and after the removal of 32-bit code the byte size is 2,085,880.
Go to Show Package Contents / Contents / MacOS / revsecurity.dylib. If revsecurity.dylib is present, you must remove the 32-bit code from that dynamic library.
sudo lipo <path_to_revsecurity.dylib> -remove i386 -output <path_to_revsecurity.dylib>
13. Remove 32-bit code from revpdfprinter.dylib
Note: not all apps will have revpdfprinter.dylib (mine does not)
Note: It is my understanding that LiveCode 9.0.1 and earlier are outputting a universal version (64-bit and 32-bit) of revpdfprinter.dylib. Future versions might cease doing that. Since my app doesn't include that code I cannot tell you how many bytes the universal and 64-bit only versions are.
I do not know where revpdfprinter.dylib is stored within the app. Perhaps it is at Go to Show Package Contents / Contents / MacOS / revpdfprinter.dylib. If revpdfprinter.dylib is present, you must remove the the 32-bit code from that dynamic library.
sudo lipo <path_to_revpdfprinter.dylib> -remove i386 -output <path_to_revpdfprinter.dylib>
14. Close any Package Contents windows.
Close all the windows where you are examining Package Contents.
15. Change the permissions of the standalone app
Change ownership of all the parts and pieces of the standalone app so that you "own" tsNet. In terminal:
sudo chmod -R u+rw <path_to_standalone_app_bundle>
16. Enable sandboxing
Enable sandboxing for the Mac App Store. Create "entitlements.plist" in the same directory as the standalone application. I duplicate the Info.plist file, change the name, and replace the <dict> section with my apps' sandbox settings.
All the standard sandbox entitlement keys are documented here.
The app sandbox temporary exception entitlement keys are documented here.
My app user selects a file, opens it, reads it, and writes to another file so I need "com.apple.security.files.user-selected.read-write".
I use "get URL" and thus need "com.apple.security.network.client".
Your sandboxed and signed app automatically has a folder you can write into for preferences:
put specialFolderPath("support") & "/<your_app_name>" into prefsfolder
You can automatically read and write to that folder. No entitlements need to be specified for that folder. For my app with an app name of "TXF Convert", prefsfolder is:
/Users/keenethery/Library/Containers/com.txfconvert.txfconvert/Data/Library/Application Support/TXF Convert
This path through "Container" is only visible to you when the app is sandboxed and signed. I write my preferences file inside the final "TXF Convert" folder inside the "Application Support" folder.
My entitlements.plist contains this dict specification.
<dict> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.files.user-selected.read-write</key> <true/> <key>com.apple.security.network.client</key> <true/> </dict>
None of the LiveCode documentation details what entitlements are required for various LiveCode functions or commands. The only way I found out what worked and what didn't was by testing the signed and sandboxed version of my app and then altering it accordingly.
Note, some entitlements are read-only with a corresponding read-write. You cannot use both. If you need read-write, do not include read-only.
17. Apple Developer Certificates
To upload to the App Store you need an Apple Developer account and corresponding developer certificates. Enroll in Apple's developer program at:
Create your Apple Developer installer and application certificate pair, and store them in your keychain on your development Mac.
18. Code sign executables
All of the executables in your app must be code signed with your Apple Developer ID certificate. Open the application "Keychain Access" in your Utilities folder. Select the keychain "login" and the category of "My Certificates". In the list should be two:
3rd Party Mac Developer Installer: <your_name> (<your_ID>)
3rd Party Mac Developer Application: <your_name> (<your_ID>)
3rd Party Mac Developer Installer: Kee Nethery (CEASNJ1234)
3rd Party Mac Developer Application: Kee Nethery (CEASNJ1234)
If you have "Developer ID Application: ..." and "Developer ID Installer: ..." in the "Certificates" section within "KeyChain Access", and no certificates within the "My Certificates" section, you will need to create the certificates that get stored in "My Certificates".
In developer.apple.com look for the area "Certificates, Identifiers and Profiles" also displayed as "Certificates, IDs and Profiles". Select that. In the upper left, select "macOS" to create macOS certificates. From that point the path is: macOS / Production / What type of certificate do you need? / Production / Mac App Store / [Continue] / Mac App Distribution. You'll create a CSR with your Apple ID email and company name that gets saved to disk. Continue, select the CSR, and it will create a file in your Downloads folder "mac_app.cer". Run through the process again and select the other choice "Mac Installer Distribution". You need both certificates. Continue in the web site and you'll be able to select them and have them be imported into your keychain so that they show up in "My Certificates".
You will need the "Application" portion to do the code sign. You will also need Xcode installed with it's utilities to perform the code sign. Using the above sample, in Terminal, code sign the app and all of the embedded executables inside it:
sudo codesign --verbose --deep --force --sign "3rd Party Mac Developer Application: Kee Nethery (CEASNJ1234)" <path_to_standalone_app_bundle>
19. Code sign entitlements.plist
Once that is done, a CodeSignature folder is created inside the app bundle. Then codesign the entitlements.plist:
sudo codesign --verbose --deep --force --sign "3rd Party Mac Developer Application: Kee Nethery (CEASNJ1234)" --entitlements <path_to_entitlements.plist_file> <path_to_standalone_app_bundle>
20. Verify the signing
Just to confirm all is well, verify the code signing:
sudo codesign --verify <path_to_standalone_app_bundle>
No response means that there were no errors and it is code signed.
21. Create the installer package
Create the installer package.
sudo productbuild --component <path_to_standalone_app_bundle> /Applications --sign "3rd Party Mac Developer Installer: Kee Nethery (CEASNJ1234)" --product <path_to_standalone_app_bundle_Info.plist> <path_to_standalone_app_bundle_delete_".app">.pkg
For that last path, it is the path to the standalone application bundle with the .app suffix replaced by .pkg
This will create a properly signed installer package file in the same folder as the application.
22. Test the installer
Test the installer. (Note the "-target /" that comes after the path to the pkg file)
sudo installer -store -pkg <path_to_app_pkg_file> -target /
It will go through the installation process to confirm that the installer package is OK.
23. Test the app
Now that the signed app is permanently embedded in the installer, you must do a final test of the signed app to make sure that it still works with all the sandbox permissions. Test everything. I ended up removing app features because they ceased working when the app was signed to operate in the sandbox.
24. Upload the app to App Store Connect
"Application Loader.app" is now in Xcode. You need the most recent Xcode version to access it. Xcode menu / Open Developer Tool menu item / Application Loader.app
Little Snitch Users: I have Little Snitch installed. The Application Loader within Xcode kept failing until I allowed incoming connections to the process "ascp". For some reason Apple servers want to connect back into my machine for the application upload. Once the upload was complete, I disabled that rule in Little Snitch.
25. Create a new version in App Store Connect
To create a new app, in developer.apple.com: App Store Connect / Go to App Store Connect / My Apps / + / New macOS App
"Company Name" is the name of your company as shown on your keychain certs, in my instance "Kee Nethery"
"Name" is the name of the app without the .app appended to it.
Bundle ID: was selectable from a list (no idea where that came from) and it had the name and the bundle ID "TXF Convert - com.txfconvert.txfconvert"
"SKU" is whatever you want it to be, it's your internal identifier. I used "txfconvert".
Once I created the new macOS app, it was assigned an Apple ID. For example, "1454474799"
Essentially you are registering a new Bundle ID so that you can then upload your .pkg file that has that Bundle ID.
Revise Existing App
To create a new version, in App Store Connect, select the previously version and hit the "(+) VERSION" button on the left side of the App Store screen. Once you've uploaded the standalone with the Application Loader, select with the (+) next to the "Build" section header to select the previously uploaded app package.
When Apple tells you to fix an error in 2.0 you'll need to increment the version number in the app (for example to 2.0.1) and in App Store Connect. You can just edit the version number in the App Store Connect data display from 2.0 to 2.0.1. Now all the app data belongs to 2.0.1.
26. Add screen shots to App Store Connect
Screen shots uploaded into App Store Connect for the App Store need to be specific screen sizes. The fewest dpi is 1280x800 and the store allows 10 screen shots. The screen shots have to be the entire screen so I created an alternate user to get clean anonymous screen shots.