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 July 2018. Because the AppStore requirements change continually, this checklist will slowly get out of date. My thanks to the LiveCode community and LiveCode support team. Much of this was passed on to me by others. 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...
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.
4. Build the standalone
Save as standalone application.
5. Remove extended attributes
LiveCode saves a bunch of extended attributes into the files in the standalone application and they must all be removed. 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>
6. Rename localization files
The AppStore 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. Set the tsNet bundler identifier
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:
11. Close any Package Contents windows.
Close all the windows where you are examining Package Contents.
12. 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>
13. Enable sandboxing
Enable sandboxing for the Mac AppStore. 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.
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.
14. Apple Developer Certificates
To upload to the App Store you need an Apple Developer account and corresponding developer certificates. This article does not cover that process. Create your Apple Developer installer and application certificate pair, and store them in your keychain on your development Mac.
15. 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)
You will need the "Application" portion to do 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>
16. 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>
17. 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.
18. 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.
19. 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.
20. 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.
21. Upload the app to iTunes Connect
Download the "Application Loader.app" from iTunesConnect. Upload the .pkg file to the iTunesConnect site.
22. Add screen shots to iTunes Connect
Screen shots uploaded into iTuneConnect for the AppStore need to be specific screen sizes. The fewest dpi is 1280x800 and the store allows 5 screen shots. The screen shots have to be the entire screen so I created an alternate user to get clean anonymous screen shots.
23. Create a new version in iTunes Connect
To create a new version, in iTunesConnect, select the previously released 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 it under the "Build" section.
To upload a modified version, ie go from unapproved 2.0 to 2.0.1, select the rejected 2.0 version, and in the Build section, remove the old version, and add the new version. The app descriptive header on the left side changes from 2.0 to 2.0.1 and now all the app data belongs to 2.0.1.