52 *
auto updater = _new<MyUpdater>();
53 * updater->handleStartup(args);
56 * _new<MainWindow>(updater)->show();
62 * You can pass updater instance to your window (as shown in the example) and display update information from
65 * # Observing update progress
66 * <b aui-src=
"aui.updater/tests/UpdaterTest.cpp#L191"></b>
71 * <b aui-src=
"aui.updater/tests/UpdaterTest.cpp#L195"></b>
73 * ## Checking
for updates
74 * <b aui-src=
"aui.updater/tests/UpdaterTest.cpp#L197"></b>
82 * a[label =
"Your App"],
83 * u[label =
"AUpdater",
URL =
"@ref AUpdater"];
84 * a -> u [label =
"handleStartup(...)",
URL =
"@ref AUpdater::handleStartup"];
85 * a <- u [label =
"status = AUpdater::StatusIdle",
URL =
"@ref AUpdater::StatusIdle"];
86 * a <- u [label =
"control flow"];
88 * --- [label=
"App Normal Lifecycle"];
90 * a -> u [label =
"checkForUpdates()",
URL =
"@ref AUpdater::checkForUpdates"];
91 * a <- u [label =
"status = AUpdater::StatusCheckingForUpdates",
URL =
"@ref AUpdater::StatusCheckingForUpdates"];
92 * a <- u [label =
"control flow"];
93 * u box u [label =
"checkForUpdatesImpl()",
URL =
"@ref AUpdater::checkForUpdatesImpl"];
94 * a <- u [label =
"status = AUpdater::StatusIdle",
URL =
"@ref AUpdater::StatusIdle"];
97 * --- [label=
"Update published"];
99 * a -> u [label =
"checkForUpdates()",
URL =
"@ref AUpdater::checkForUpdates"];
100 * a <- u [label =
"status = AUpdater::StatusCheckingForUpdates",
URL =
"@ref AUpdater::StatusCheckingForUpdates"];
101 * a <- u [label =
"control flow"];
102 * u box u [label =
"checkForUpdatesImpl()",
URL =
"@ref AUpdater::checkForUpdatesImpl"];
103 * u box u [label =
"update was found"];
104 * a <- u [label =
"status = AUpdater::StatusIdle",
URL =
"@ref AUpdater::StatusIdle"];
108 * You might want to store update check results (i.e., download url) in your implementation of
111 * ## Downloading the update
112 * <b aui-src=
"aui.updater/tests/UpdaterTest.cpp#L234"></b>
114 * When an update is found, your app should call @ref
AUpdater::downloadUpdate to download and unpack the update. It is
117 * @ref UPDATER_WORKFLOWS
for more information about update workflow decisions). It calls
122 * a[label =
"Your App"],
123 * u[label =
"AUpdater",
URL =
"@ref AUpdater"];
125 * a -> u [label =
"downloadUpdate()",
URL =
"@ref AUpdater::downloadUpdate"];
126 * a <- u [label =
"status = AUpdater::StatusDownloading",
URL =
"@ref AUpdater::StatusDownloading"];
127 * u box u [label =
"downloadUpdateImpl()",
URL =
"@ref AUpdater::downloadUpdateImpl"];
128 * a <- u [label =
"status = AUpdater::StatusWaitingForApplyAndRestart",
URL =
"@ref AUpdater::StatusWaitingForApplyAndRestart"];
129 * --- [label=
"Your App Prompts User to Update"];
133 * ## Applying (deploying) the update
134 * <b aui-src=
"aui.updater/tests/UpdaterTest.cpp#L255"></b>
138 * newer copy of your app downloaded before with a special command line argument which is handled by
139 * @ref
AUpdater::handleStartup in that executable. The initial app process is finished, closing your app window as
140 * well. From now, your app is in
"downtime" state, so we need to apply the update and reopen app back again as quickly
141 * as possible. This action is required to perform update installation. The copy then replaces old application (where it
142 * actually installed) with itself (that is, the downloaded, newer copy). After operation is complete, it passes the
143 * control back to the updated application executable. At last, the newly updated application performs a cleanup after
147 * a[label =
"Your App"],
148 * u[label =
"AUpdater",
URL =
"@ref AUpdater"],
149 * da[label =
"Newer Copy of Your App"],
150 * du[label =
"AUpdater in App Copy",
URL =
"@ref AUpdater"];
151 * a :> u [label =
"applyUpdateAndRestart()",
URL =
"@ref AUpdater::applyUpdateAndRestart"];
152 * u :> da [label =
"Execute with update arg"];
153 * u box u [label =
"exit(0)"];
154 * a box u [label =
"Process Finished"];
155 * da box du [label =
"Process Started"];
156 * da -> du [label =
"handleStartup",
URL =
"@ref AUpdater::handleStartup"];
157 * du box du [label =
"AUpdater::deployUpdate(...)",
URL =
"@ref AUpdater::deployUpdate"];
158 * a <: du [label =
"Execute"];
159 * du box du [label =
"exit(0)"];
160 * da box du [label =
"Process Finished"];
161 * a box u [label =
"Process Started"];
162 * a -> u [label =
"handleStartup",
URL =
"@ref AUpdater::handleStartup"];
163 * u box u [label =
"cleanup download dir"];
164 * a box u [label=
"App Normal Lifecycle"];
168 * After these operations complete, your app is running in its normal lifecycle.
170 *
AUpdater is an abstract
class; it needs some functions to be implemented by you.
172 * In
this example,
let's implement auto update from GitHub release pages.
173 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L298"></b>
174 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L299"></b>
175 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L300"></b>
176 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L301"></b>
178 * static constexpr auto LOG_TAG = "MyUpdater";
179 * class MyUpdater: public AUpdater {
181 * ~MyUpdater() override = default;
184 * AFuture<void> checkForUpdatesImpl() override {
187 * auto githubLatestRelease = aui::updater::github::latestRelease("aui-framework", "example_app");
188 * ALogger::info(LOG_TAG) << "Found latest release: " << githubLatestRelease.tag_name;
189 * auto ourVersion = aui::updater::Semver::fromString(AUI_PP_STRINGIZE(AUI_CMAKE_PROJECT_VERSION));
190 * auto theirVersion = aui::updater::Semver::fromString(githubLatestRelease.tag_name);
192 * if (theirVersion <= ourVersion) {
193 * getThread()->enqueue([] {
195 * nullptr, "No updates found", "You are running the latest version.", AMessageBox::Icon::INFO);
199 * aui::updater::AppropriatePortablePackagePredicate predicate {};
200 * auto it = ranges::find_if(
201 * githubLatestRelease.assets, predicate, &aui::updater::github::LatestReleaseResponse::Asset::name);
202 * if (it == ranges::end(githubLatestRelease.assets)) {
203 * ALogger::warn(LOG_TAG)
204 * << "Newer version was found but a package appropriate for your platform is not available. "
206 * << predicate.getQualifierDebug() << ", got: "
207 * << (githubLatestRelease.assets |
208 * ranges::view::transform(&aui::updater::github::LatestReleaseResponse::Asset::name));
211 * ALogger::info(LOG_TAG) << "To download: " << (mDownloadUrl = it->browser_download_url);
213 * getThread()->enqueue([this, self = shared_from_this(), version = githubLatestRelease.tag_name] {
214 * if (AMessageBox::show(
215 * nullptr, "New version found!", "Found version: {}\n\nWould you like to update?"_format(version),
216 * AMessageBox::Icon::INFO, AMessageBox::Button::YES_NO) != AMessageBox::ResultButton::YES) {
223 * } catch (const AException& e) {
224 * ALogger::err(LOG_TAG) << "Can't check
for updates:
" << e;
225 * getThread()->enqueue([] {
227 * nullptr, "Oops!
", "There is an error occurred
while checking
for updates. Please
try again later.
",
228 * AMessageBox::Icon::CRITICAL);
234 * AFuture<void> downloadUpdateImpl(const APath& unpackedUpdateDir) override {
237 * AUI_ASSERTX(!mDownloadUrl.empty(), "make a successful call to checkForUpdates first
");
238 * downloadAndUnpack(mDownloadUrl, unpackedUpdateDir);
239 * reportReadyToApplyAndRestart(makeDefaultInstallationCmdline());
240 * } catch (const AException& e) {
241 * ALogger::err(LOG_TAG) << "Can
't check for updates: " << e;
242 * getThread()->enqueue([] {
244 * nullptr, "Oops!", "There is an error occurred while downloading update. Please try again later.",
245 * AMessageBox::Icon::CRITICAL);
252 * AString mDownloadUrl;
256 * # Updater workflows {#UPDATER_WORKFLOWS}
257 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L397"></b>
258 * When using AUpdater for your application, you need to consider several factors including usability, user experience,
259 * system resources, and particular needs of your project.
261 * Either way, you might want to implement a way to disable auto update feature in your application.
263 * ## Prompt user on every step
264 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L403"></b>
266 * This approach is implemented in AUI's @ref example_app_template.
268 * The updater checks
for updater periodically or upon user request and informs the user that an update is available.
269 * The user then decides whether to proceed with update or not. If they agree the application will download and install
272 * This way can be considered as better approach because the user may feel they control the situation and the
273 * application never does things that user never asked to (trust concerns). On the other hand, such requirement of
274 * additional user interaction to can distract them from doing their work, so these interactions should not be annoying.
276 * You should not use
AMessageBox (unless user explicitly asked to check
for update) as it literally interrupts the
277 * user
's workflow, opting them to make a decision before they can continue their work. A great example of a bad auto
278 * update implementation is qBittorrent client on Windows: hence this application typically launches on OS startup,
279 * it checks for updates in background and pops the message box if update was found, **even if user is focused on
280 * another application or away from keyboard**.
283 * <b aui-src="aui.updater/tests/UpdaterTest.cpp#L421"></b>
285 * This approach is implemented in @ref example_app_auigram, as well as in official Qt-based Telegram Desktop client.
287 * The updater silently downloads the update in the background while the user continues working within the application
288 * or even other tasks. The update then is applied automatically upon restart.
289 * Optionally, the application might show a button/message/notification bubble to restart and apply update.
291 * Despite user trust concerns, this approach allows seamless experience - users don't need to be interrupted during
292 * their work. They even might not care about updates.
Represents a value that will be available at some point in the future.
Definition AFuture.h:621
An add-on to AString with functions for working with the path.
Definition APath.h:128
Updater class.
Definition AUpdater.h:30
virtual void applyUpdateAndRestart()
Deploy the downloaded update.
AProperty< std::any > status
State of the updater.
Definition AUpdater.h:207
virtual AFuture< void > downloadUpdateImpl(const APath &unpackedUpdateDir)=0
Performs update delivery to the specified directory.
virtual void handleStartup(const AStringVector &applicationArguments)
Performs a pre-application AUpdater routine.
virtual AFuture< void > checkForUpdatesImpl()=0
Check for updates user's implementation.
void checkForUpdates()
Sets status to StatusCheckingForUpdates and calls checkForUpdatesImpl, implemented by user.
void downloadUpdate()
Sets status to StatusDownloading and calls downloadUpdateImpl, implemented by user.
void downloadAndUnpack(AString downloadUrl, const APath &unpackedUpdateDir)
Typical download and unpack implementation.
type_of< T > t
Selects views that are of the specified C++ types.
Definition type_of.h:71
#define let
Performs multiple operations on a single object without repeating its name (in place) This function c...
Definition kAUI.h:262
#define AUI_ENTRY
Application entry point.
Definition Entry.h:90
#define async
Executes following {} block asynchronously in the global thread pool. Unlike asyncX,...
Definition kAUI.h:329
@ URL
Optimize for URLs.
Definition ATextInputType.h:48
Displaying native modal message dialogs.
Definition AMessageBox.h:46