Library / SDK
alex-spataru/QSimpleUpdater avatar
alex-spataru/QSimpleUpdater

QSimpleUpdater, a Qt update library that expects you to host the manifest

Updater system for Qt applications

1,109 stars281 forksC++NOASSERTION

At a glance

What is it?
QSimpleUpdater gives Qt 5.15 and Qt 6 applications a self-hosted update path: it fetches a JSON manifest, compares version strings, and either drives the download and install itself or hands everything back to your code through signals. It is a library with no server component and no released packages.
Who is it for?
QSimpleUpdater suits teams shipping a desktop application on Windows, macOS or Linux who want to keep control of the update server and the update prompt, and who can host a small JSON file. It does not suit applications that need staged rollouts, differential downloads, code signing verification handled for them, or an update channel they cannot host themselves, because the library ships no service and no package.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A JSON manifest is the entire update protocol

QSimpleUpdater is an auto-updating library for Qt applications, written in C++17 and requiring Qt 5.15 or Qt 6 with the Core, Gui, Network and Widgets modules. The problem it solves is narrow and familiar: a desktop application needs to discover that a newer build exists, tell the user, fetch it, and start the installer, and most applications end up writing that logic badly.

The mechanism is a single JSON file hosted somewhere the application can reach. The file describes the latest version and the download locations per platform, and the library downloads it, compares the remote version against the local one, and if the remote one is greater it reports an available update. There is no account system, no update channel configuration service, and no telemetry. The application is expected to be the only client of a file the application owner already controls.

That constraint is the design, not an oversight. The library also supports checking updates for different modules of an application independently, so a suite of tools sharing one manifest can each track its own version and its own download link.

One singleton, and every setting scoped to a URL

The entry point is a singleton, and the API shape around it is unusual in a useful way. You call `QSimpleUpdater::getInstance()`, then configure it per update URL before asking it to check:

cpp
#include <QSimpleUpdater.h>

const QString url = "https://example.com/updates.json";

auto* updater = QSimpleUpdater::getInstance();
updater->setModuleVersion(url, "1.2.0");  // Optional: defaults to qApp version
updater->checkForUpdates(url);

Every option takes that URL as its first argument. Notifications, the downloader, the install behaviour and the module version are all keyed by the manifest location rather than by module, so two different manifests can behave differently inside the same process. If you want to compare against a version other than the running application version, `setModuleVersion` overrides the default, which the comment in the example marks as optional.

The library also emits signals, and that is the escape hatch. The documentation states that you can re-implement the notifications yourself by reacting to those signals, and that you can toggle which notifications appear:

cpp
QString url = "https://example.com/updates.json";

QSimpleUpdater::getInstance()->setNotifyOnUpdate(url, true);
QSimpleUpdater::getInstance()->setNotifyOnFinish(url, false);
QSimpleUpdater::getInstance()->checkForUpdates(url);

So the built-in prompt is a starting point, not a commitment. An application that wants its own update dialog can switch off the built-in notifications and take over, or keep the downloader and replace only the dialog.

Manifest fields, and the platform key that trips people up

The manifest is a JSON object with an `updates` map keyed by platform. Each entry carries the version, the download location, a changelog and a flag for whether the update can be skipped:

json
{
  "updates": {
    "windows": {
      "open-url": "",
      "latest-version": "2.0.0",
      "download-url": "https://example.com/app-setup.exe",
      "changelog": "<p>Bug fixes and improvements.</p>",
      "mandatory-update": false
    }
  }
}

The `changelog` field is HTML-formatted and shown to the user, so a plain string will render as markup rather than as text. `open-url` short-circuits the download entirely and opens a page in a browser instead, which is the right choice when the download lives behind a login or a store link.

The platform keys are `windows`, `osx`, `linux`, `ios` and `android`. Note that macOS is `osx`, not `macos`. If the key does not match the running platform, the library simply finds no entry, and because the failure is a missing map lookup rather than an error, the symptom is an application that never reports an update. `setPlatformKey()` lets you override the key, which is the documented escape for custom targets.

The `mandatory-update` flag has real consequences. When it is true the user cannot skip the update, and the application quits if the user cancels. That is the mechanism behind a forced upgrade, and it is worth being deliberate about, since a mandatory update pushed with a broken build leaves users unable to start the application.

Version strings are compared semantically, with pre-release rules

The comparison logic is exposed as a static function, `QSimpleUpdater::compareVersions(remote, local)`, so you can use it independently of the update flow. The rules follow semantic versioning with optional pre-release suffixes, and the documentation lists the cases explicitly:

- `1.2.3` against `1.2.4` is a patch upgrade - `v1.0.0-alpha1` against `v1.0.0` puts the stable release ahead of the pre-release - `v1.0.0-alpha1` against `v1.0.0-beta1` puts beta ahead of alpha - `v1.0.0-rc9` against `v1.0.0-rc10` compares the suffix numbers numerically

A `v` prefix is optional and ignored during comparison, so the manifest can carry `1.2.0` or `v1.2.0` without mattering. What the library does not document is a tolerance for build metadata, which under semantic versioning is supposed to be ignored during comparison. If your release identifiers carry a build number and your local version string carries a different one, the behaviour is undefined by the documentation and worth checking in your own build pipeline before you rely on it.

Adding it to a project: CMake, qmake, and the test suite

CMake is the recommended route, and the library is designed to be embedded as a subdirectory rather than installed system-wide. The example shows the two options you would normally turn off, and then the link against your target:

cmake
set(QSIMPLE_UPDATER_BUILD_TUTORIAL OFF CACHE BOOL "" FORCE)
set(QSIMPLE_UPDATER_BUILD_TESTS OFF CACHE BOOL "" FORCE)

add_subdirectory(3rd-party/QSimpleUpdater)
target_link_libraries(YourApp PRIVATE QSimpleUpdater)

The options are worth reading before you commit. `QSIMPLE_UPDATER_BUILD_TUTORIAL` defaults to off and `QSIMPLE_UPDATER_BUILD_TESTS` defaults to on, `QSIMPLE_UPDATER_BUILD_SHARED` switches the library from static to shared, and `QSIMPLE_UPDATER_INSTALL` is automatic: rules are generated when the project is built top-level and suppressed when it is embedded with `add_subdirectory()`. `QSU_QT_VERSION_MAJOR` is there to skip Qt auto-detection when you know you are on 5 or 6.

If your project still uses qmake, the procedure is shorter: copy the QSimpleUpdater folder into your 3rd-party directory, include the QSimpleUpdater.pri file with the `include()` function, and the tutorial project in the repository serves as the reference.

To build the library with its own tests, the commands are:

bash
cmake -B build -DQSIMPLE_UPDATER_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure

The tests directory in the repository layout confirms the unit tests are a first-class part of the project rather than an afterthought.

Where this stops short of a hosted update service

The alternative approach to the same problem is a hosted update service, where a vendor operates the manifest, the release channels, the staged rollout and the client download for you. The difference in approach is one of ownership. QSimpleUpdater assumes the manifest is a file you host and that the update pipeline is a URL you pass in, and it gives you signals and per-URL switches for every visible behaviour, but it offers nothing for staged rollout, for multiple channels, or for the server side of the distribution.

That boundary also determines the failure modes. Because the manifest is a plain HTTP GET, a typo in the URL, a server that returns an HTML error page instead of JSON, or a host that is briefly unreachable all end up as a failed update check, and the documentation says nothing about retries, caching or offline behaviour. The client also has no built-in notion of verifying that a downloaded package is signed or intact, so the security of the update channel rests on your hosting and on your installer.

The library can also drive the download and launch the installer for you, or stop at the point where it hands everything to your own code. Which of those you want depends on whether you can accept the default install behaviour on each platform, and on how much of the update flow you need to own.

Licence labelling, packaging, and an unversioned repository

The licence story needs a direct look. GitHub's licence field for this repository points at a custom licence the site cannot classify, with the licence file in the repository as the reference, while the badge in the README says MIT and the file in the root is named LICENSE.md. Those two signals are not the same statement, and the documentation does not explain the arrangement. If you are considering vendoring this code into a commercial application, read LICENSE.md rather than relying on either label, and settle the obligations with the maintainer if the text turns out to be a custom one.

On packaging, there is no published release. The repository has no tagged releases, no version scheme for the library itself, and no package on a registry, so integration means vendoring the source as a subdirectory or copying it into your 3rd-party folder. That has an upside, since you get the whole library at a known commit, and a downside, since you own the upgrade when the manifest format or the API changes.

The repository also ships a CLAUDE.md, a CODE_OF_CONDUCT.md and a .clang-format file, which is a sign of a project that expects outside contributions and consistent formatting. The last push was on 2026-07-02, and the repository is not archived, so it is worth checking that date yourself before planning an integration around it.

Editorial conclusion

QSimpleUpdater suits teams shipping a desktop application on Windows, macOS or Linux who want to keep control of the update server and the update prompt, and who can host a small JSON file. It does not suit applications that need staged rollouts, differential downloads, code signing verification handled for them, or an update channel they cannot host themselves, because the library ships no service and no package. Before adopting it, check the licence file directly, since the repository is tagged with a custom licence GitHub cannot classify while the README badge says MIT, and confirm that the platform key matches your build, since the manifest uses `osx` rather than `macos` and a wrong key means a silent no-op. The last push was on 2026-07-02 and there are no tagged releases, so the tree itself is your only reference point.

Frequently asked questions

How does QSimpleUpdater check for updates?

It downloads a JSON update definition file that lists the latest version, download links and changelogs per platform, then compares the remote version against the local one and notifies the user if the remote version is greater. You can register your own platform key with setPlatformKey().

What are the requirements for using QSimpleUpdater?

Qt 5.15 or Qt 6 with the Core, Gui, Network and Widgets modules, and C++17. The library builds as a static library by default and can be switched to shared with the QSIMPLE_UPDATER_BUILD_SHARED CMake option.

Can I use my own update dialog instead of the built-in notification?

Yes. The library emits signals, and the documentation says you can re-implement the notifications by reacting to them. You can also toggle the built-in ones per URL with setNotifyOnUpdate and setNotifyOnFinish.

Which key do I use for macOS in the updates JSON file?

The key is `osx`. The documented platform keys are `windows`, `osx`, `linux`, `ios` and `android`, and a key that does not match the running platform means no update is found for that platform.

What licence does QSimpleUpdater use?

The repository is tagged with a custom licence that GitHub cannot classify, pointing at the licence file in the repository, and the README badge shows MIT. The documentation does not explain the difference, so read the licence file in the repository root before redistributing the code.

Official sources

  1. alex-spataru/QSimpleUpdater on GitHub
  2. Issues
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/alex-spataru-qsimpleupdater.svg)](https://hysenlabs.com/projects/alex-spataru-qsimpleupdater)

Was this useful?