Sparkle: How to use with a C++ Qt application?

Created on 12 Oct 2017  路  10Comments  路  Source: sparkle-project/Sparkle

How can I use Sparkle with my Qt C++ application? All of the documentation is referencing XCode, but I build on macOS with Qt Creator.

Most helpful comment

Use Sparkle in Qt5 Application

Install Sparkle

Sparkle framework originally created by Andy Matuschak became the de facto standard for software updates on macOS. It's not hard to use it with QT applications as well, although may not be too straightforward. This page will document the steps, which was a result of research in Google and lots of experiments.

The latest version of Sparkle is distributed via a compressed file, which can be downloaded from this web site:
http://sparkle-project.org/. The distribution file contains a couple of utility scripts in bin folder for signing updates, a test app and Sparkle.framework file.

After the distribution file is downloaded, unpack it to a local folder. For example:

~/apps/Sparkle-1.18.1

so that, the utility scripts can be used later. Also please copy Sparkle.framework to $HOME/Qt/5.9.2/clang_64/lib (replace the Qt version number as needed), which will be used in different QT projects.

Configure Sparkle QT Project

In your Qt project file, add the following configuration in mac section, because this is specific to macOS only. (for Windows, WinSparkle is a very good option to go BTW)

mac {
    QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../Frameworks
    LIBS += -framework AppKit
    LIBS += -framework Carbon
    LIBS += -framework Foundation
    LIBS += -framework ApplicationServices
    LIBS += -framework Sparkle

    # Other mac related configuration ...
    QMAKE_MAC_SDK = macosx10.13
    QMAKE_INFO_PLIST = Info.plist
    ...
}

Use Sparkle in QT Project

Please check the following files in the sample project.

AutoUpdater.h
AutoUpdater.cpp
AutoUpdaterSparkle.h
AutoUpdaterSparkle.mm
CocoaInitializer.h
CocoaInitializer.cpp

The source code of this classes can be found from this github repository: https://github.com/jiakuan/mixing-cocoa-and-qt

Add Code in main Method

    AutoUpdater* updater = 0;
#ifdef Q_OS_MAC
    CocoaInitializer initializer;
    bool updateEnabled = settings.value(Constants::PREF_UPDATE_ENABLED, true).toBool();
    int updateInterval = settings.value(Constants::PREF_UPDATE_INTERVAL, 3600).toInt();
    if (updateEnabled) {
        updater = new AutoUpdaterSparkle("https://url/to/appcast-mac.xml");
        QtLog::debug() << "Checking for updates...";
        updater->checkForUpdatesInBackground();
        updater->setUpdateCheckInterval(updateInterval);
    } else {
        QtLog::debug() << "Auto-update is disabled on Mac OS X.";
    }
#endif
    app.exec();
    // Delete update on app quit, or maybe use a unique_ptr
    delete updater;

You may also want to have a Check for Updates... menu item. When it's clicked, the following code can be used to execute the update checking.

void MyMainWindow::checkForUpdates() {
    if(updater) {
        QtLog::info("checkForUpdates with UI...");
        updater->checkForUpdatesWithUI();
    } else {
        QtLog::info("no updater initialised.");
    }
}

Copy Sparkle.Framework in Package Script

In your package script, after the macOS app bundle has been created, you need to copy the Sparkle framework in.

mkdir -p ${DIST_DIR}/${APPNAME}.app/Contents/Frameworks/Sparkle.framework && \
cp -R $HOME/apps/Sparkle-1.18.1/Sparkle.framework ${DIST_DIR}/${APPNAME}.app/Contents/Frameworks && \

After that, you will be able to run the app and test auto-update feature on macOS.

References

I hope it helps! Please feel free to let me know if you encounter any other issues. I'm happy to help.

All 10 comments

I鈥檝e been using it with QT and Qt Creator etc successfully since a few years ago. Not so hard to do so. I will document something when I have some time on weekend :) Keep watching here.

In unrelated notes, you might also want to check out QtAutoUpdater if you deploy with Qt Installer Framework. There is also Fervor that is essentially a Sparkle clone for Qt, but the project seems to be abandoned.

Nice, thanks, keen to see the blog post!

I've already got the backend stuff sorted out, just need a painless to update the actual binary now.

Use Sparkle in Qt5 Application

Install Sparkle

Sparkle framework originally created by Andy Matuschak became the de facto standard for software updates on macOS. It's not hard to use it with QT applications as well, although may not be too straightforward. This page will document the steps, which was a result of research in Google and lots of experiments.

The latest version of Sparkle is distributed via a compressed file, which can be downloaded from this web site:
http://sparkle-project.org/. The distribution file contains a couple of utility scripts in bin folder for signing updates, a test app and Sparkle.framework file.

After the distribution file is downloaded, unpack it to a local folder. For example:

~/apps/Sparkle-1.18.1

so that, the utility scripts can be used later. Also please copy Sparkle.framework to $HOME/Qt/5.9.2/clang_64/lib (replace the Qt version number as needed), which will be used in different QT projects.

Configure Sparkle QT Project

In your Qt project file, add the following configuration in mac section, because this is specific to macOS only. (for Windows, WinSparkle is a very good option to go BTW)

mac {
    QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../Frameworks
    LIBS += -framework AppKit
    LIBS += -framework Carbon
    LIBS += -framework Foundation
    LIBS += -framework ApplicationServices
    LIBS += -framework Sparkle

    # Other mac related configuration ...
    QMAKE_MAC_SDK = macosx10.13
    QMAKE_INFO_PLIST = Info.plist
    ...
}

Use Sparkle in QT Project

Please check the following files in the sample project.

AutoUpdater.h
AutoUpdater.cpp
AutoUpdaterSparkle.h
AutoUpdaterSparkle.mm
CocoaInitializer.h
CocoaInitializer.cpp

The source code of this classes can be found from this github repository: https://github.com/jiakuan/mixing-cocoa-and-qt

Add Code in main Method

    AutoUpdater* updater = 0;
#ifdef Q_OS_MAC
    CocoaInitializer initializer;
    bool updateEnabled = settings.value(Constants::PREF_UPDATE_ENABLED, true).toBool();
    int updateInterval = settings.value(Constants::PREF_UPDATE_INTERVAL, 3600).toInt();
    if (updateEnabled) {
        updater = new AutoUpdaterSparkle("https://url/to/appcast-mac.xml");
        QtLog::debug() << "Checking for updates...";
        updater->checkForUpdatesInBackground();
        updater->setUpdateCheckInterval(updateInterval);
    } else {
        QtLog::debug() << "Auto-update is disabled on Mac OS X.";
    }
#endif
    app.exec();
    // Delete update on app quit, or maybe use a unique_ptr
    delete updater;

You may also want to have a Check for Updates... menu item. When it's clicked, the following code can be used to execute the update checking.

void MyMainWindow::checkForUpdates() {
    if(updater) {
        QtLog::info("checkForUpdates with UI...");
        updater->checkForUpdatesWithUI();
    } else {
        QtLog::info("no updater initialised.");
    }
}

Copy Sparkle.Framework in Package Script

In your package script, after the macOS app bundle has been created, you need to copy the Sparkle framework in.

mkdir -p ${DIST_DIR}/${APPNAME}.app/Contents/Frameworks/Sparkle.framework && \
cp -R $HOME/apps/Sparkle-1.18.1/Sparkle.framework ${DIST_DIR}/${APPNAME}.app/Contents/Frameworks && \

After that, you will be able to run the app and test auto-update feature on macOS.

References

I hope it helps! Please feel free to let me know if you encounter any other issues. I'm happy to help.

Can you please add this as a pull request for the online documentation? See https://github.com/sparkle-project/sparkle-project.github.io

That鈥檚 a good idea, will do when I find some time soon.

Thanks so much for the instructions - I'm working through integrating the changes, my code is over here.

I was able to avoid copying Sparkle.framework into Qt libs - which'll be a pain for CI and new contributors - by doing LIBS += -F../3rdparty/sparkle (insert your Sparkle.framework location here).

The check-in files have also slightly different names - not AutoUpdaterSparkle but SparkleAutoUpdater. CocoaInitializer.cpp is now also CocoaInitializer.mm instead.

I'm stuck at this error now, however:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -c -pipe -stdlib=libc++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.9  -Wall -Wno-deprecated -Wno-unused-local-typedefs -Wno-unused-parameter -O3 -std=gnu++11 -w -fPIC -DBUILD_IRC_CORE -DBUILD_IRC_MODEL -DBUILD_IRC_UTIL -DQS_LOG_LINE_NUMBERS_SHORT -DAPP_VERSION=\"3.4.0\" -DAPP_BUILD=\"-dev\" -DAPP_TARGET=\"Mudlet\" -DLUA_DEFAULT_PATH=\"\" -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_UITOOLS_LIB -DQT_WIDGETS_LIB -DQT_MULTIMEDIA_LIB -DQT_GAMEPAD_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB -I. -I../3rdparty/communi/src/core -I../3rdparty/communi/include/IrcCore -I../3rdparty/communi/src/model -I../3rdparty/communi/include/IrcModel -I../3rdparty/communi/src/util -I../3rdparty/communi/include/IrcUtil -I../3rdparty/dblsqd -I../3rdparty/luazip -I../3rdparty/edbee-lib/edbee-lib -I../3rdparty/edbee-lib/vendor/qslog -I../3rdparty/edbee-lib/vendor/onig -I../3rdparty/edbee-lib/vendor/onig/enc/unicode -I/usr/local/include -I/usr/local/Cellar/hunspell/1.6.1/include/hunspell -I/usr/local/include/lua-5.1 -I/usr/local/Cellar/yajl/2.1.0/include/yajl -I/usr/local/Cellar/pcre/8.40/include -I/usr/local/Cellar/libzip/1.1.2/include -I/usr/local/Cellar/libzip/1.1.2/lib/libzip/include -I../../Qt/5.8/clang_64/lib/QtOpenGL.framework/Headers -I../../Qt/5.8/clang_64/include -I../../Qt/5.8/clang_64/include/QtUiTools -I../../Qt/5.8/clang_64/lib/QtWidgets.framework/Headers -I../../Qt/5.8/clang_64/lib/QtMultimedia.framework/Headers -I../../Qt/5.8/clang_64/lib/QtGamepad.framework/Headers -I../../Qt/5.8/clang_64/lib/QtGui.framework/Headers -I../../Qt/5.8/clang_64/lib/QtNetwork.framework/Headers -I../../Qt/5.8/clang_64/lib/QtConcurrent.framework/Headers -I../../Qt/5.8/clang_64/lib/QtCore.framework/Headers -I. -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/OpenGL.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/AGL.framework/Headers -I. -I../../Qt/5.8/clang_64/mkspecs/macx-clang -F/Users/mudlet/Qt/5.8/clang_64/lib -o SparkleAutoUpdater.o ../3rdparty/sparkle-glue/SparkleAutoUpdater.mm
../3rdparty/sparkle-glue/SparkleAutoUpdater.mm:8:10: fatal error: 'Sparkle/Sparkle.h' file not found
#include <Sparkle/Sparkle.h>
         ^
1 error generated.
make: *** [SparkleAutoUpdater.o] Error 1

Sparkle.h is not inside a folder called Sparkle, it's inside a folder called A - which is a pretty weird name and I'm dubious about updating the inclusion. Any ideas?

@jiakuan your repository has the same error:

Mudlets-iMac:~ mudlet$ git clone https://github.com/jiakuan/mixing-cocoa-and-qt.git
Cloning into 'mixing-cocoa-and-qt'...
remote: Counting objects: 25, done.
remote: Total 25 (delta 0), reused 0 (delta 0), pack-reused 25
Unpacking objects: 100% (25/25), done.
Mudlets-iMac:~ mudlet$ cd mixing-cocoa-and-qt/
Mudlets-iMac:mixing-cocoa-and-qt mudlet$ qmake
Info: creating stash file /Users/mudlet/mixing-cocoa-and-qt/.qmake.stash
Mudlets-iMac:mixing-cocoa-and-qt mudlet$ make
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -c -pipe -stdlib=libc++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.9  -F. -O2 -std=gnu++11 -Wall -W -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I../Qt/5.8/clang_64/lib/QtWidgets.framework/Headers -I../Qt/5.8/clang_64/lib/QtGui.framework/Headers -I../Qt/5.8/clang_64/lib/QtCore.framework/Headers -I. -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/OpenGL.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/AGL.framework/Headers -I../Qt/5.8/clang_64/mkspecs/macx-clang -F/Users/mudlet/Qt/5.8/clang_64/lib -o AutoUpdater.o AutoUpdater.cpp
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -c -pipe -stdlib=libc++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.9  -F. -O2 -std=gnu++11 -Wall -W -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I../Qt/5.8/clang_64/lib/QtWidgets.framework/Headers -I../Qt/5.8/clang_64/lib/QtGui.framework/Headers -I../Qt/5.8/clang_64/lib/QtCore.framework/Headers -I. -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/OpenGL.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/AGL.framework/Headers -I../Qt/5.8/clang_64/mkspecs/macx-clang -F/Users/mudlet/Qt/5.8/clang_64/lib -o main.o main.cpp
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -c -pipe -stdlib=libc++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.9  -F. -O2 -std=gnu++11 -Wall -W -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I../Qt/5.8/clang_64/lib/QtWidgets.framework/Headers -I../Qt/5.8/clang_64/lib/QtGui.framework/Headers -I../Qt/5.8/clang_64/lib/QtCore.framework/Headers -I. -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/OpenGL.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/AGL.framework/Headers -I../Qt/5.8/clang_64/mkspecs/macx-clang -F/Users/mudlet/Qt/5.8/clang_64/lib -o SparkleAutoUpdater.o SparkleAutoUpdater.mm
SparkleAutoUpdater.mm:8:10: fatal error: 'Sparkle/Sparkle.h' file not found
#include <Sparkle/Sparkle.h>
         ^
1 error generated.
make: *** [SparkleAutoUpdater.o] Error 1

I've fixed it:

    SPARKLE_PATH = $$PWD/../3rdparty/sparkle
    QMAKE_LFLAGS += -F $$SPARKLE_PATH
    QMAKE_OBJECTIVE_CFLAGS += -F $$SPARKLE_PATH

Don't need the QMAKE_POST_LINK commands either, this works fine:

    sparkle.path = Contents/Frameworks
    sparkle.files = $$SPARKLE_PATH/Sparkle.framework
    QMAKE_BUNDLE_DATA += sparkle

Great, thanks for sharing.
The task of updating documentation is still in my TODO list, just super busy these days.

In the meantime, if you see anything else we can improve in the documentation, please let me know. It will be helpful for us when we want to check in the future, also will be helpful for others.

Was this page helpful?
0 / 5 - 0 ratings