Submiting a Qt App to Mac App Store

Maybe you already thought about creation a Qt application for Mac App Store or submitting an existing one. While I am on vacations I made a probe of such possibility – you may know that Apple and Nokia developing now different ecosystems of languages/libraries and you may expect to have troubles of bypassing Qt application into Mac App Store. Anyway I found that it is not so complicated as expected, though it took time to do all preparations, my application passed and was published in store with first attempt.

For long time I had one application in my attic. It was an interesting experiment for studying and remembering foreign words. This application do kind of training by repeating words and force you to type correct spelling while application pronounce it to you using TTS (surely you need high-quality voice engine – I used voices from Infovox iVox). That was pretty effective and helped me a lot for studying new words and forming active vocabulary for Norwegian. Because of hearing, reading and typing it makes very effective associations in your brains. The application is cross-platform and requires only QtGui, QtXml and QtCore, also qt plugins are not need.


First I worked on adjustments of widgets and look-and-feel to have app closer to Mac style. It wasn’t complicated because on each release Nokia improves drawing of widgets to have it very close to native views. You can find differences in user interface if you know especialities.

Then application (bundle) was build with inclusion of all required libs as private frameworks. I got too much for such small application — about 60MB. For me it is too heavy and I decided to make it smaller. Also after checking Qt forums I found that some patches are required for Qt to get a ticket into store.

It was chosen Carbon version of Qt because I found some glitches in GUI when used Cocoa – so I decided not to spend time on it. Then disabled everything that is not needed in configure, also used 10.5 sdk and only 32 bit:

./configure -carbon -fast -no-qt3support -no-xmlpatterns -no-multimedia -no-audio-backend -no-phonon -no-phonon-backend -no-svg -no-webkit -no-javascript-jit -no-script -no-scripttools -no-declarative -no-openssl -arch "x86" -sdk "/Developer/SDKs/MacOSX10.5.sdk" -no-exceptions

I got about 30MB for Frameworks. But Framework folders can be downsized — no need for debug versions of libs, no need for header files because frameworks are private. Because I used copying of Frameworks using QMAKE_POST_LINK, it looks like this:

QMAKE_POST_LINK = \
mkdir -p $${BUNDLETARGET}/Contents/Frameworks; \
cp -R $${QTFRAMEWORKSPATH}/QtCore.framework \
$${QTFRAMEWORKSPATH}/QtGui.framework \
$${QTFRAMEWORKSPATH}/QtXml.framework \
$${BUNDLETARGET}/Contents/Frameworks; \
...
\
strip $(TARGET); \
strip -x $${BUNDLETARGET}/Contents/Frameworks/QtCore.framework/Versions/4/QtCore; \
strip -x $${BUNDLETARGET}/Contents/Frameworks/QtGui.framework/Versions/4/QtGui; \
strip -x $${BUNDLETARGET}/Contents/Frameworks/QtXml.framework/Versions/4/QtXml; \
\
rm -rf `find $${BUNDLETARGET} -name "*.prl"`; \
rm -rf `find $${BUNDLETARGET} -name "*.lproj"`; \
rm -rf `find $${BUNDLETARGET} -name "*_debug*"`; \
rm -rf `find $${BUNDLETARGET} -name "Headers"`; \
\
otool -L $(TARGET); \
otool -L $${BUNDLETARGET}/Contents/Frameworks/QtCore.framework/Versions/4/QtCore; \
otool -L $${BUNDLETARGET}/Contents/Frameworks/QtGui.framework/Versions/4/QtGui; \
otool -L $${BUNDLETARGET}/Contents/Frameworks/QtXml.framework/Versions/4/QtXml; \
\
echo "Ok"

Result — 12MB, in Mac App Store – just 5.3MB. This is great just for small application which is based on non-native GUI frameworks.

So we resolved size issue, now it is time to get Qt ready to pass examination and be ready for store.

First — app should keep own data in ~/Library/Application Support/com.organization.appname, so we download patch mac-app-store-cache-location.diff. After applying it, when using QDesktopServices::storageLocation() we get right path to location where to keep app/user data.

Second — Qt apps always read and write ~/Library/Preferences/com.nokia.qt.plist. You can not disable it due to need of common settings of Qt Applications. But Apple has restrictions for applications not to write settings of other applications. So we download second patch for Qt — mac-settings-in-app-area.diff, and in main() in very beginning add a call: qt_force_trolltech_settings_to_app_area(true);

Also there are two patches — first one is for removal Hide/Show All from application menu, but I found this patch later, though my app passed control without it, and second one is for SQLite — but I did not study this one (no need for my app).

Frameworks should be adjusted to have them referenced to each other using @executable_path. You can see it easily by otool utility:

$ otool -L QtGui.framework/QtGui
QtGui.framework/QtGui:
/usr/local/Trolltech/Qt-4.7.3/lib/QtGui.framework/Versions/4/QtGui (compatibility version 4.7.0, current version 4.7.3)
/usr/local/Trolltech/Qt-4.7.3/lib/QtCore.framework/Versions/4/QtCore (compatibility version 4.7.0, current version 4.7.3)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 136.0.0)
...

Add this to QMAKE_POST_LINK:

install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore \
$${BUNDLETARGET}/Contents/Frameworks/QtCore.framework/Versions/4/QtCore; \
install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui \
$${BUNDLETARGET}/Contents/Frameworks/QtGui.framework/Versions/4/QtGui; \
install_name_tool -id @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml \
$${BUNDLETARGET}/Contents/Frameworks/QtXml.framework/Versions/4/QtXml; \
...
+and same for other references

Now everything is fine:

otool -L QtGui.framework/QtGui
QtGui.framework/QtGui:
@executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui (compatibility version 4.7.0, current version 4.7.3)
@executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore (compatibility version 4.7.0, current version 4.7.3)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 136.0.0)
...

Now just by copying .app bundle onto another Mac without developer tools and Qt your App should start without problems.

Also you need to sign your app by your signature(certificate) as Mac developer. You have to get such certificate from developer.apple.com, pay $99 for year subscription if you haven’t done it yet.

/usr/bin/codesign -f -s 3rd\ Party\ Mac\ Developer\ Application:\ Firstname\ Lastname $${BUNDLETARGET}; \

and add this to “pro” file too.

But for submitting the application into App Store, I had to start Xcode, build it in dsym+dwarf mode, make it signed, added prepared Frameworks by «New Build Phase -> New Copy Files Build Phase». Everything was tested with otool to see all dependencies, checked on another Mac and got ready to send into App Store (If you know how to do all such stuff by scripts please let me know – prefer to do everything in Qt Creator).

Summary —
1. It is not so complicated as for first sight.
2. Apple is okay to accept Qt apps, just need to apply some patches to Qt.
3. Size of Qt App (bundle) is small enough (maybe if you include several resources into app bundle then size of Qt frameworks may become a minor part).

Result — http://itunes.apple.com/us/app/togmeg/id445287955

References:
Main source of information — http://bugreports.qt.nokia.com/browse/QTBUG-16549

This entry was posted in Blog, MacAppStore, Projects, Qt, QtSpeech, TogMeg, TTS. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

17 Comments

  1. Daniel Albuschat
    Posted June 30, 2011 at 06:35 | Permalink

    Unrelated to the actual topic – do you know about Anki (http://ankisrs.net/)? It is awesome and maybe the best language learning application out there.

  2. mschlander
    Posted June 30, 2011 at 08:34 | Permalink

    How about the licensing? Are the terms of the Mac App Store compatible with e.g. (L)GPL?

  3. yshurik
    Posted June 30, 2011 at 11:46 | Permalink

    Daniel: it looks like little different principe used in anki, anyway, thanks for link, good to know

  4. Jon
    Posted July 1, 2011 at 22:47 | Permalink

    Is this available for linux too? I’d love to use it if it reads out the words, that really sticks in my head.

  5. eric
    Posted July 3, 2011 at 21:56 | Permalink

    i am trying to prepare my qt app for the mac app but not really successful yet..

    i am using 4.7.3 i am not really sure how to use those patches .. can you give more details, do you have to recompile qt?

    how do you deal with the certTool ?

    thanks in advance

  6. yshurik
    Posted July 5, 2011 at 21:20 | Permalink

    mschlander: surely compatible, but LGPL only. Pure GPL components are not compatible as I know.

  7. Chen Wang
    Posted July 7, 2011 at 11:26 | Permalink

    this is a good news for me, I can upload my app to mac app store.
    I have a question, how to implement left navigation bar in http://lynxline.com/wp-content/uploads/2011/06/TogMeg-Screenshot-1.png?
    Thanks very much.

  8. yshurik
    Posted July 7, 2011 at 11:36 | Permalink

    Well, this is just QTreeView with some style adjustments (background, border, etc).

  9. Chen Wang
    Posted July 8, 2011 at 03:07 | Permalink

    Thanks again ,I have finish it independently.
    ^_^

  10. Mason
    Posted July 16, 2011 at 09:35 | Permalink

    I tested qt_force_trolltech_settings_to_app_area, but the file is still created under ~/Library/Preferences, checked the patch in corelib for many times, still no success. I tried to use qWarning to output some strings in qt_force_trolltech_settings_to_app_area and SettingsPrivate::create. And verified that the patch has been applied.

  11. Oliver Knoll
    Posted August 25, 2011 at 11:53 | Permalink

    Why do you need to compile in XCode again as a final step? Because of the DWARF compiler settings? Why don’t you just add the appropriate compiler flags, as suggested by:

    https://bugreports.qt.nokia.com/browse/QTBUG-16549?focusedCommentId=137653&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-137653

    The example application attached to the mentioned bug happens to apply those setttings as well (although I can’t remember whether I applied the step “then you need to generate the DSYM files QMAKE_POST_LINK=’/Developer/usr/bin/dsymutil -o “).

  12. Oliver Knoll
    Posted August 25, 2011 at 11:58 | Permalink

    Regarding the DWARF compiler settings, from the example attachement:

    # Required steps for Apple Desktop App Store
    QMAKE_CFLAGS += “-gdwarf-2″
    QMAKE_POST_LINK += dsymutil \”$${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}\”;

    Doesn’t that work for you?

    [https://bugreports.qt.nokia.com/secure/attachment/19565/2010-01-15-MacStoreApp.zip]

  13. Oliver Knoll
    Posted August 25, 2011 at 12:04 | Permalink

    And one More Thing(tm): “Also you need to sign your app by your signature(certificate) as Mac developer. You have to get such certificate from developer.apple.com, pay $99 for year subscription if you haven’t done it yet.”

    Since one needs the 99$ subscription anyway as to be able to deploy into the Mac App Store my question is rather academic, but just curios: are you sure you need a certificate (signed) by Apple?

    From the public Apple developer docs: “However, if the only reason you need a certificate is for a signing identity to use with Mac OS X, you can create your own identity by using the Certificate Assistant, which is provided as part of the Keychain Access application. ”

    with the subsequent caveats: “A self-signed certificate created with the Certificate Assistant is not recognized by users’ operating systems as a valid certificate for any purpose other than validating the designated requirement of your signed code.”

    Chapter “Obtaining a Signing Identity” in:

    http://developer.apple.com/library/mac/#documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW2

    Sounds to me like you can create and sign your Certificate yourself, no? Or are there Mac App Store specific requirements (which are non-public) which require you to have your Certificate signed by Apple, as you suggest?

    Thanks, Oliver

  14. yshurik
    Posted August 25, 2011 at 16:54 | Permalink

    > Why do you need to compile in XCode again as a final step? Because of the DWARF compiler settings?

    Not really because of dwarf, but I was stuck to find a way how to create “archive” of application which you have to send for validation.

  15. yshurik
    Posted August 25, 2011 at 17:18 | Permalink

    > Sounds to me like you can create and sign your Certificate yourself, no? Or are there Mac App Store specific requirements (which are non-public) which require you to have your Certificate signed by Apple, as you suggest?

    Yeah, I guess they do this check to know that you paid money to them

  16. David
    Posted December 8, 2011 at 01:24 | Permalink

    Looks like the guidelines have changed since this blog was written.

    I just had an app rejected because it wrote to this location: ~/Library/Preferences/
    Doesn’t matter that it was wasn’t a trolltech file.

    See location guidelines here: http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html#//apple_ref/doc/uid/TP40010572

    Don’t know the solution yet … researching…

  17. David
    Posted December 17, 2011 at 01:43 | Permalink

    Actually – you can delete the last comment. The app was rejected because the file written to in ~/Library/Preferences/ didn’t match my bundle id. At least – when I changed it to that format it was accepted. Apple was not clear on this point.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>