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 December 2018. Because the AppStore requirements change continually, this checklist will slowly get out of date, experience bit-rot. My thanks to the LiveCode community and LiveCode support team. Much of this 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. 

https://youtu.be/WRJE4TivKAc

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>

for example:

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...

https://youtu.be/wUhbB-ixjaM

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

https://youtu.be/dHqyGygtSQA

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 1.0.0.0 which violates that rule. A version number of the format 1.0.0 is acceptable.

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

https://youtu.be/8UG3aqWSQ_A

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

https://youtu.be/DYi-teC-qUg

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

https://youtu.be/J8zBrqamNyc

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

https://youtu.be/TqNy7hMtKcc

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:

	<key>ITSAppUsesNonExemptEncryption</key>
	<false/>

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

https://youtu.be/uVWtwA4Pv28

In Info.plist one must add a value for LSApplicationCategoryType. The various possible values are listed here.

For my app, I added:

	<key>LSApplicationCategoryType</key>
	<string>public.app-category.finance</string>

10. 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.

https://youtu.be/Dll9BqvHePM

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:

	<key>CFBundleIdentifier</key>
	<string>com.txfconvert.txfconvert.tsNet</string>

11. 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>

12. 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>

13. Close any Package Contents windows.

Close all the windows where you are examining Package Contents.

14. Change the permissions of the standalone app

https://youtu.be/8YNtU62FfYQ

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>

15. Enable sandboxing

https://youtu.be/IuZjoLZ462U

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.

https://youtu.be/Bhpne2lRK4M

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.

16. 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:

https://developer.apple.com/programs/enroll/

Create your Apple Developer installer and application certificate pair, and store them in your keychain on your development Mac.

17. Code sign executables

https://youtu.be/TmTqOVTXQMA

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>)

For example:

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>

18. Code sign entitlements.plist

https://youtu.be/6qa5lzNeBQI

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>

19. Verify the signing

https://youtu.be/MkYJMSyDb3g

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.

20. Create the installer package

https://youtu.be/XSOI_vsBxig

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  

For example:

      LiveCode Projects/myApp.app

would become:

      LiveCode Projects/myApp.pkg

This will create a properly signed installer package file in the same folder as the application.

21. Test the installer

https://youtu.be/evEEjjVka-E

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.

22. 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.

23. Create a new version in App Store Connect

https://youtu.be/htMTB7XfH8M

New App

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 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.

24. Upload the app to App Store Connect

https://youtu.be/H1xrzvp_lK0

"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

25. Add screen shots to App Store Connect

https://youtu.be/Og90dY1uF94

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.

0 Comments

Add your comment

E-Mail me when someone replies to this comment