How to Publish Qt Applications in the Mac App Store

 

We have worked together with the Qt Project in order to enable smooth publishing of Qt-based applications to the Mac App Store with Qt Commercial 4.8.1. I have made an example submission, and wrote some instructions for those who want to submit their Qt apps to Mac App Store. To prove that this really works, I have successfully submitted one Qt-based application built with Qt Commercial 4.8.1 to the Mac App Store. Read on for details.

 

Note: This blog is best viewed with Mozilla Firefox. Internet Explorer is giving errors on the code snippet.

 

How to get your Qt app to the Mac App Store

 

Qt and Qt Commercial 4.8.1 include fixes to issues that have caused problems in Mac App Store submissions. Issues with using file dialogs in sandboxed applications have been fixed, global Qt settings can be stored in the application-local settings file and the hide and show menu items in the application menu are now enabled and disabled correctly.

 

This blog post contains a summary of how to prepare a Qt application for successful Mac App Store submission. Full details about the submission process can be found in the Apple document “Submitting to the Mac App Store”.

 

Global Qt settings storage

 

By default, Qt stores global settings in ~/Application Support/Preferences/com.trolltech.plist. This violates the Mac App Store file system usage requirements, as the settings file must be named using the application's bundle identifier, its name or the company's name. Qt 4.8.1 stores the global Qt settings in the application-local settings file in two situations:

 

1. When the application runs in a sandbox or
2. When the application's Info.plist contains a key named "ForAppStore" with the value "yes".

 

In these situations, the application's bundle identifier is used as the name of the settings file, and the global Qt settings are stored in a settings group called "QtLibrarySettings". If you are using sandboxing, the settings will automatically be stored in the correct file, otherwise you should insert the “ForAppStore” key with the value “yes” in the application’s Info.plist to get the correct behaviour.

 

Using file save dialogs in a sandbox

 

The file save dialog requires a non-empty default filename to work correctly inside a sandbox. You can supply a default filename to the save dialog as follows:

 

QString fileSavePath = QFileDialog::getSaveFileName(this, "Save file", "default filename");

Or if you create the dialog instance yourself:

 

QFileDialog *fileSaveDialog = new QFileDialog(this);
fileSaveDialog->setAcceptMode(QFileDialog::AcceptSave);
fileSaveDialog->selectFile("default filename");


File open dialogs do not require any special set-up to work correctly.

 

Data and cache storage locations

 

If you are using QDesktopServices::storageLocation() with DataLocation or CacheLocation, you should ensure that the application and organization names used by Qt match the values in iTunes Connect. If the values do not match, the paths that storageLocation() returns will violate the Mac App Store file system usage requirements. You can set the application and organization names that Qt uses by calling these methods in your main() function before the QApplication instance is created:

 

QApplication::setOrganizationName("MyCompany")
QApplication::setApplicationName("MyApp")

 

One minor caveat here is that you cannot use localized organization or application names in your Qt application, since it's not possible to localize these names in iTunes Connect.

 

Info.plist and application icon

 

A custom Info.plist file instead of the qmake-generated one is needed, since the Mac App Store requires some specific keys to be set that are not present in the generated file. Name the file e.g. MyAppInfo.plist, since qmake will overwrite the file if it's named Info.plist. Add the following line to your .pro file to get MyAppInfo.plist to be copied into the application bundle as Info.plist:

 

QMAKE_INFO_PLIST = MyAppInfo.plist


The documentation about the required Info.plist contents can be found in the “Submitting to the Mac App Store” document.

 

You'll also need to provide an icon for your application by adding the following line to the .pro file:

 

ICON = MyApp.icns


Click here to read more information about using an application icon in Qt.

 

Debug symbols

 

To generate the debug symbol information needed for the Mac App Store submission, add these settings to your .pro file:

 

QMAKE_CFLAGS += -gdwarf-2
QMAKE_CXXFLAGS += -gdwarf-2


The debug symbols can be extracted with the dsymutil command as follows:

 

dsymutil MyApp.app/Contents/MacOS/MyApp -o MyApp.app.dSYM


Code signing

 

Prior to codesigning the application bundle, use macdeployqt to copy the Qt frameworks and plug-ins into the bundle. If you know that your application does not use certain Qt frameworks or plug-ins, you can remove them from the bundle to reduce its total size. Besides the application bundle, you should also sign the frameworks and plug-ins in the bundle; at the time of writing, this is not required for App Store approval, but warnings about unsigned frameworks are issued. Instructions for obtaining the code signing certificates can be found in the “Submitting to the Mac App Store” document on the Apple developer website.

 

Frameworks are code signed with e.g.

 

codesign -s "3rd Party Mac Developer Application: Developer Name" MyApp.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore


The application bundle is signed as follows:

 

codesign -s "3rd Party Mac Developer Application: Developer Name" --entitlements MyEntitlements.plist MyApp.app


You can leave out "--entitlements MyEntitlements.plist" if you are not using sandboxing. Documentation for codesigning can be found in Apple’s “Code Signing Guide”, and additional information in Apple’s Technical Note 2206.

 

Building the installer package

 

The installer package is built with the productbuild command.

 

productbuild --component MyApp.app /Applications --sign "3rd Party Mac Developer Installer: Developer Name" MyApp.pkg


You can test the installer by running

 

sudo installer -store -pkg MyApp.pkg -target /

 

Project file example

 

The following .pro file snippet contains the required settings and creates make targets for code signing and product package generation.

 

macx {
    # Name of the application signing certificate
    APPCERT = "3rd Party Mac Developer Application: <yourcompany>"

    # Name of the installer signing certificate
    INSTALLERCERT = "3rd Party Mac Developer Installer: <yourcompany>"
   
    # Bundle identifier for your application
    BUNDLEID = com.yourcompany.MyApp
   
    # Name of the entitlements file (only needed if you want to sandbox the application)
    ENTITLEMENTS = Entitlements.plist
   
    ICON = MyApp.icns

     QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
    QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO
    QMAKE_OBJECTIVE_CFLAGS_RELEASE =  $$QMAKE_OBJECTIVE_CFLAGS_RELEASE_WITH_DEBUGINFO
    QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO

    QMAKE_INFO_PLIST = MyAppInfo.plist
    OTHER_FILES += MyAppInfo.plist
                 $${ENTITLEMENTS}

    codesign.depends  += all
    codesign.commands += macdeployqt $${TARGET}.app;
   
    # Remove unneeded frameworks (uncomment and change to suit your application)
    #codesign.commands += rm -r $${TARGET}.app/Contents/Frameworks/QtDeclarative.framework;
   
    # Remove unneeded plug-ins (uncomment and change to suit your application)
    #codesign.commands += rm -r $${TARGET}.app/Contents/PlugIns/accessible;
   
    # Extract debug symbols
    codesign.commands += dsymutil $${TARGET}.app/Contents/MacOS/$${TARGET} -o $${TARGET}.app.dSYM;
   
    # Sign frameworks and plug-ins (uncomment and change to suit your application)
    #codesign -s "$APPCERT" -i $BUNDLEID $${TARGET}.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore
    #codesign -s "$APPCERT" -i $BUNDLEID $${TARGET}.app/Contents/PlugIns/imageformats/libqjpeg.dylib
   
    # Sign the application bundle, using the provided entitlements
    codesign.commands += codesign -f -s $${APPCERT} -v --entitlements $${ENTITLEMENTS} $${TARGET}.app;

    product.depends += all
   
    # Build the product package
    product.commands += productbuild --component $${TARGET}.app /Applications --sign $${INSTALLERCERT} $${TARGET}.pkg;

    QMAKE_EXTRA_TARGETS += codesign product copyfiles
}

 

Copy this snippet to the .pro file of your application, and edit the certificate names, plist file name and bundle identifier to match your build environment. After building the application, you can sign the application bundle with

 

make codesign

and build the product package with

make product

Remember to test installation of the product package by running

sudo installer -store -pkg MyApp.pkg -target /

Your application should now be good for submitting to the Mac App Store.

 

Example submission

 

To verify that these instructions work, I have made a submission of one example application to the Mac App Store.  You can find the Pixelator example application built with Qt Commercial 4.8.1 from the Mac App Store here. I modified the original example slightly to provide a better user experience, and edited the .pro file as described above. The application was accepted to the Mac App Store without any issues.

 

If you have any questions, feel free to contact us.


Comments