Deploying simple "Hello world" QT Python App to App Store

Good evening,

I'm trying to deploy an extremely simple Python QT app to the app store: it's roughly ~10 lines of code that shows a window containing a"Hello, world!" message.

This seemingly trivial task of deploying a "Hello World" app to the store has been extremely difficult, perhaps because of my lack of familiarity with the Apple ecosystem.

I'd like to demonstrate all of the steps I've taken, my current understanding of deployments, and what the problems are.

Project files

Here's the code for the app (in a file located at src2/test_app/app.py).

import os, sys
from PySide2.QtWidgets import *


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setCentralWidget(QLabel("Hello, world!"))


if __name__ == '__main__':
    os.environ["QT_MAC_WANTS_LAYER"] = "1"
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

Furthermore, I've created an assets folder which contains an icon.icns file and I also created a Pyinstaller config/test_app.spec file to bundle this app into a testapp.app package:

block_cipher = None

added_files = [
    ('../assets', 'assets')
]

a = Analysis(
    ['../src2/test_app/app.py'],
    pathex=[],
    binaries=[],
    datas=added_files,
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False
 )

pyz = PYZ(
    a.pure,
    a.zipped_data,
    cipher=block_cipher
)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='testapp',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon='../assets/64.icns'
)


coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='app'
)

app = BUNDLE(
    coll,
    name='testapp.app',
    icon='../assets/icon.icns',
    bundle_identifier='com.stormbyte.test-app.pkg',
    info_plist={
        'NSPrincipalClass': 'NSApplication',
        'NSAppleScriptEnabled': False,
        'LSBackgroundOnly': False,
        'LSApplicationCategoryType': 'public.app-category.utilities',
        'NSRequiresAquaSystemAppearance': 'No',
        'CFBundlePackageType': 'APPL',
        'CFBundleSupportedPlatforms': ['MacOSX'],
        'CFBundleIdentifier': 'com.stormbyte.test-app.pkg',
        'CFBundleVersion': '0.0.1',
    }
)

In addition, I have an config/entitlements.plist file, containing all of the necessary information for binaries built by pyinstaller:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <!-- These are required for binaries built by PyInstaller -->
        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
        <true/>
        <key>com.apple.security.cs.disable-library-validation</key>
        <true/>
        <key>com.apple.security.app-sandbox</key>
        <true/>
</dict>
</plist>

This entitlements file is apparently necessary for pyinstaller to run things on Mac successfully, which is why I included it.

Building the .APP

I am able to build this app with:

pyinstaller \
  --noconfirm \
  --log-level WARN \
  --distpath '/Users/nikolay/Desktop/projects/cling_wrap/dist' \
  --workpath '/Users/nikolay/Desktop/projects/cling_wrap/build' \
  './config/test_app.spec'

This creates a testapp.app file in my dist folder and the file icon I've designated appears just as I've intended.

Codesigning the .APP binaries

Next, I am able to use the codesign tool to sign all of the files that are part of the testapp.app I've just created.

codesign \
  -vvv \
  --strict \
  --deep \
  --force \
  --timestamp \
  --options runtime \
  --entitlements './config/entitlements.plist' \
  --sign "Developer ID Application: Nikolay ***** (Z57YJ*****)" \
  '/Users/nikolay/Desktop/projects/cling_wrap/dist/testapp.app'

This successfully executes.

Building the .PKG Installer

Next, I am able to use productbuild to create a .PKG file, which will allow a user to install the app:

productbuild \
  --version '0.0.1' \
  --sign "Developer ID Installer: Nikolay **** (Z57YJ*****)" \
  --component '/Users/nikolay/Desktop/projects/cling_wrap/dist/testapp.app' \ 
  /Applications testapp.pkg 

This successfully outputs:

...
productbuild: Adding certificate "Developer ID Certification Authority"
productbuild: Adding certificate "Apple Root CA"
productbuild: Wrote product to testapp.pkg

When I attempt to run this installer, everything works.

I can even see the app installed in my Applications folder, and I can double-click and run it:

With this confirmed, I am ready to run the notarization & stapling process prior to submitting my app to the App Store.

I run notarization using the xcrun tool:

xcrun altool \
  --notarize-app \
  --primary-bundle-id com.stormbyte.test-app.pkg \
  --username=*****@gmail.com \
  --password **** \
  --file '/Users/nikolay/Desktop/projects/cling_wrap/testapp.pkg'

Which outputs:

No errors uploading '/Users/nikolay/Desktop/projects/cling_wrap/testapp.pkg'.
RequestUUID = c40ebde4-dcd1-***********

I then receive an e-mail from Apple telling me that the notorization process has been successful:

Next, I run the stapler tool:

xcrun stapler staple '/Users/nikolay/Desktop/projects/cling_wrap/testapp.pkg'

Which is also successful.

Finally, I attempt to use Transporter to upload my app to the store but this happens:

It says that the icon failed (when it clearly exists and is recognized by Mac?!!) and that the signature is invalid?

Am I using the incorrect certificates for distribution to the App store?

Thanks!

Post not yet marked as solved Up vote post of nikolay_a Down vote post of nikolay_a
3.6k views

Replies

I was able to execute productbuild using a different certificate, namely the 3rd party developer certificate. Also, I watched a video on Notarization, and it says the notarization is only for apps distributed outside the app store?

https://developer.apple.com/videos/play/wwdc2021/10261/

Is this true? So this means that I do not need to notarize or staple my app if I am uploading it to the App store directly?

I attempted to follow this assumption and skipped notorization altogether, opting to simply run the following productbuild command and then use Transporter:

productbuild \
  --version '0.0.1' \
  --sign "3rd Party Mac Developer Installer: Nikolay **** (Z57YJ*****)" \
  --component '/Users/nikolay/Desktop/projects/cling_wrap/dist/testapp.app' \
  /Applications testapp.pkg

I'm now just stuck on this one error:

I resolved this final issue by ensuring that I had a folder (/Users/nikolay/Desktop/projects/cling_wrap/assets/icon.iconset) which contained the following 2 files:

  • icon_512x512.png (with a resolution of 512pixels by 512pixels, obviously)
  • icon_512x512@2x.png (with a resolution of 1024pixels by 1024 pixels)

Then I used the following command to regenerate the icon.icns file:

iconutil --convert icns assets/icon.iconset

With this, Transporter accepted the app and was able to upload it:

I've started writing a tutorial on uploading a pyinstaller app to the Mac App Store: https://github.com/nyavramov/python_app_mac_app_store

I hope this will be useful to someone. I'll add more details in the coming days, but if anyone spots inaccuracies or things I should add, please let me know!

I've started writing a tutorial on uploading a pyinstaller app to the Mac App Store …

Cool. I’m sure a lot of folks will appreciate that.


Some random comments on stuff in your earlier emails.

<!-- These are required for binaries built by PyInstaller -->

I recommend that you avoid comments in your .entitlements file. If you include them, make sure to normalise your entitlements file before passing it to codesign. For more on that, see the Normalise the Entitlements Property List section of Resolving Code Signing Crashes on Launch.

com.apple.security.cs.allow-unsigned-executable-memory

You should talk to the Python folks about this. A modern language runtime shouldn’t need this, but rather should be able to get away with much more secure com.apple.security.cs.allow-jit.

com.apple.security.cs.disable-library-validation

You should only need this if you’re loading code from other third-party developers, like plug-ins. In a typical app, all the code is either signed by you or signed by Apple and thus you can leave library validation enabled.

Moreover, there are good reasons to leave it enabled:

Next, I am able to use the codesign tool to sign all of the files that are part of the testapp.app I've just created.

codesign \
  -vvv \
  --strict \
  --deep \
  …

Do not use --deep when signing code. For more details, see --deep Considered Harmful. For general advice on code signing and packaging macOS apps, see:

I run notarization using the xcrun tool:

xcrun altool \
  …

altool is deprecated for the purposes of notarisation and will stop working in Fall 2023. If you’re currently notarising with altool, switch to notarytool now. For more information about notarytool, watch WWDC 2021 Session 10261 Faster and simpler notarization for Mac apps.

it says the notarization is only for apps distributed outside the app store?

Correct.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I see the Github Page for Python Mac App Store. It looks like you have solved all of the technical challenges, congratulations!

I notice you have not specified a license file, which I believe is an obstacle to deploying Qt without buying a $1000+ Qt Commercial license. In fact if you go to https://www.qt.io/product/features and select MacOS as your deployment platform, it greys out nearly everything, no matter what license you select. )

Did you actually get this app published on the App Store? If so, can you provide a link to the App Store? (preferably on the GitHub Readme).

Alright, Nice Work, and thank you for sharing it!