当前位置:   article > 正文

使用Sparkle框架MacOS程序升级_spustandardupdatercontroller

spustandardupdatercontroller

本地环境

使用CMake编译的QT项目,版本:Sparkle 2

简介

Sparkle是一个非常简单且易用的macOS应用程序更新框架,目前众多知名macOS App都在使用。

Sparkle的原理是根据提前配置好的xml文件地址,每次启动后解析xml,看看有没有比当前版本新的数据,有的话提示更新。 xml文件可以存在任何可以访问xml元数据的服务器,包括 GitHub 仓库。

一、下载编译Sparkle

1.1 下载Sparkle

github地址:https://github.com/sparkle-project/Sparkle

git clone --recursive https://github.com/sparkle-project/Sparkle

1.2 编译Sparkle

使用Xcode打开项目文件

1.可以选择支持两种架构的版本编译

2.点击运行按钮进行编译

1.3 编译运行generate_keys生成公钥和私钥

该脚本目的生成公钥和私钥

根据提示信息将公钥添加到Info.plist

1.4编译generate_appcast

该脚本目的自动生成appcast.xml

./generate_appcast myapp_updates

myapp_updates是一个文件夹

  1. myapp_updates/
  2. ├── MyApp_1.2.0.zip
  3. ├── MyApp_1.2.0.html
  4. ├── appcast.xml
  • MyApp_1.2.0.zip: 这是新版本1.2.0的更新归档文件,用户将通过Sparkle框架下载这个文件来升级到新版本。
  • MyApp_1.2.0.html: 这是1.2.0版本的HTML格式发行说明,可能包含富文本和格式化的更改列表。
  • appcast.xml: 这是由generate_appcast工具生成或更新的文件,包含了所有可用更新的元数据。在运行工具之前,这个文件可能不存在,或者需要更新。

检查最终生成的appcast.xml

1.3 拷贝生成的Sparkle文件

1.3.1 找到编译文件生成目录

1.3.2 拷贝Sparkle.framework目录到你的项目下

二、引入Sparkle到项目中

2.1 修改CMakeLists.txt

  1. # 添加 Sparkle.framework
  2. set(SPARKLE_FRAMEWORK_DIR ${CMAKE_SOURCE_DIR}/你的目录/Sparkle.framework)
  3. set(SPARKLE_INCLUDE_DIR ${SPARKLE_FRAMEWORK_DIR}/Headers)
  4. include_directories(${SPARKLE_INCLUDE_DIR})
  5. add_executable(MyProject
  6. AutoUpgrade.h
  7. AutoUpgrade.cpp
  8. #与C++的桥接文件
  9. MacosAutoUpgrade.mm
  10. MacosAutoUpgrade.h
  11. )
  12. add_compile_options(-x objective-c++)
  13. target_link_libraries(Project PRIVATE ${SPARKLE_FRAMEWORK_DIR})

2.2 代码编写

2.2.1 object-C文件
  1. #ifdef __APPLE__
  2. #include "MacosAutoUpgrade.h"
  3. #import <Sparkle/Sparkle.h>
  4. #include <QPushButton>
  5. @interface AppUpdaterDelegate : NSObject <SPUUpdaterDelegate>
  6. @property(nonatomic) SPUStandardUpdaterController *updaterController;
  7. @property(nonatomic) QObject *updaterObject;
  8. @end
  9. @implementation AppUpdaterDelegate
  10. - (instancetype)initWithUpdaterObject:(QObject *)updaterObject {
  11. self = [super init];
  12. if (self) {
  13. _updaterObject = updaterObject;
  14. }
  15. return self;
  16. }
  17. // 检查key至
  18. - (void)observeValueForKeyPath:(NSString *)keyPath
  19. ofObject:(id)object
  20. change:(NSDictionary<NSKeyValueChangeKey, id> *)change
  21. context:(void *)context {
  22. if ([keyPath isEqualToString:NSStringFromSelector(@selector(canCheckForUpdates))]) {
  23. bool canCheckForUpdates = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
  24. QMetaObject::invokeMethod(_updaterObject, "updateButtonState", Q_ARG(bool, canCheckForUpdates));
  25. } else {
  26. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  27. }
  28. }
  29. - (void)dealloc {
  30. @autoreleasepool {
  31. [_updaterController.updater removeObserver:self
  32. forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))];
  33. }
  34. }
  35. @end
  36. Updater::Updater(QPushButton *checkForUpdatesButton) {
  37. @autoreleasepool {
  38. _updaterDelegate = [[AppUpdaterDelegate alloc] initWithUpdaterObject:this];
  39. _updaterDelegate.updaterController =
  40. [[SPUStandardUpdaterController alloc] initWithStartingUpdater:YES
  41. updaterDelegate:_updaterDelegate
  42. userDriverDelegate:nil];
  43. connect(checkForUpdatesButton, &QPushButton::clicked, this, &Updater::checkForUpdates);
  44. [_updaterDelegate.updaterController.updater
  45. addObserver:_updaterDelegate
  46. forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))
  47. options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
  48. context:nil];
  49. }
  50. startAutomaticChecks();
  51. }
  52. // 主动检查更新,只要有最新版本无论是否点击忽略版本都展示升级界面
  53. void Updater::checkForUpdates() {
  54. @autoreleasepool {
  55. [_updaterDelegate.updaterController checkForUpdates:nil];
  56. }
  57. }
  58. // 自动检测更新时,如果待升级的版本已点击忽略,则不展示升级界面
  59. void Updater::startAutomaticChecks() {
  60. @autoreleasepool {
  61. // 开启自动检查更新
  62. _updaterDelegate.updaterController.updater.automaticallyChecksForUpdates = YES;
  63. // 每天检查一次
  64. _updaterDelegate.updaterController.updater.updateCheckInterval = 86400;
  65. // 已忽略的版本不展示升级界面
  66. [_updaterDelegate.updaterController.updater checkForUpdatesInBackground];
  67. }
  68. }
  69. void Updater::updateButtonState(bool canCheckForUpdates) {
  70. Q_EMIT buttonStateUpdated(canCheckForUpdates);
  71. }
  72. #endif
2.2.2 头文件
  1. #ifndef MACOS_AUTO_UPGRADE_H
  2. #define MACOS_AUTO_UPGRADE_H
  3. #include <QObject>
  4. class QPushButton;
  5. #ifdef __OBJC__
  6. @class AppUpdaterDelegate;
  7. #endif
  8. class Updater : public QObject {
  9. Q_OBJECT
  10. public:
  11. #ifdef __APPLE__
  12. Updater(QPushButton *checkForUpdatesButton);
  13. void startAutomaticChecks();
  14. signals:
  15. void buttonStateUpdated(bool canCheckForUpdates);
  16. private slots:
  17. void checkForUpdates();
  18. void updateButtonState(bool canCheckForUpdates);
  19. private:
  20. #ifdef __OBJC__
  21. AppUpdaterDelegate *_updaterDelegate;
  22. #else
  23. void *_updaterDelegate;
  24. #endif
  25. #endif
  26. };
  27. #endif // MACOS_AUTO_UPGRADE_H
2.2.3调用代码
  1. std::unique_ptr<Updater> auto_upgrade_ = nullptr;
  2. auto_upgrade_ = std::make_unique<Updater>(ui->pushButton);
  3. auto_upgrade_->startAutomaticChecks();
  4. QObject::connect(auto_upgrade_.get(), &Updater::buttonStateUpdated,
  5. ui->pushButton, &QPushButton::setEnabled);

2.3 修改info.plist

  1. <!-- 用于比较版本 -->
  2. <key>CFBundleVersion</key>
  3. <string>1.0.2</string>
  4. <!-- 应用程序的最低系统要求 -->
  5. <key>LSMinimumSystemVersion</key>
  6. <string>12.5</string>
  7. <!-- 这个键用于启用或禁用自动更新检查。如果设置为 true,Sparkle 将自动检查更新 -->
  8. <key>SUEnableAutomaticChecks</key>
  9. <true/>
  10. <!-- 增加自动更新,无需交互选项 -->
  11. <key>SUAllowsAutomaticUpdates</key>
  12. <true/>
  13. <!-- 自动更新检查间隔时间(秒) -->
  14. <key>SUScheduledCheckInterval</key>
  15. <integer>86400</integer>
  16. <!-- 这是Sparkle用来检查更新的URL。指向一个XML文件,该文件包含应用程序的最新版本信息 -->
  17. <key>SUFeedURL</key>
  18. <string>https://yourwebsite.com/updates/appcast.xml</string>
  19. <!-- generate_keys生成的公钥 -->
  20. <key>SUPublicEDKey</key>
  21. <string>your public key</string>

三、制作升级包流程

  • 编译程序生成.app文件
  • .app文件签名
  • .app文件压缩为zip/tar
  • 新建文件夹./upgrade
  • 移动压缩包到./upgrade目录下
  • 新建说明文件.txt
  • 移动说明文件.txt到./upgrade目录下
  • 执行./generate_appcast ./upgrade生成appcast.xml
  • 修改xml内部信息适配项目
  • 上传.xml 压缩文件和 .txt文件到对应位置即可使用(可以上传github测试)

四、问题排查

如果升级失败,可以使用控制台查看具体报错信息。

 

1.查看用户日志

2.点击开始(抓取日志)

3.运行更新程序(等待更新失败)

4.点击暂停,停止抓取新日志

5.搜索你的项目名,查看对应升级模块日志查看错误

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/911918
推荐阅读
相关标签
  

闽ICP备14008679号