Skip to content

Instantly share code, notes, and snippets.

@zorgiepoo
Last active June 14, 2022 04:43
Show Gist options
  • Save zorgiepoo/8a1af8cc5ae6878fa10433b3099771b3 to your computer and use it in GitHub Desktop.
Save zorgiepoo/8a1af8cc5ae6878fa10433b3099771b3 to your computer and use it in GitHub Desktop.
Hack to emulate Sparkle 1.x installUpdatesIfAvailable. Please don't use it. Use SUAutomaticallyUpdate key for installing updates silently instead or roll out your own custom UI if needed instead. This approach can break in future versions because SPUStandardUserDriver provides no subclassing guarantees. But here it is for illustration purposes.
#import <Foundation/Foundation.h>
#import <Sparkle/Sparkle.h>
@interface AutoUserDriver : SPUStandardUserDriver <SPUUserDriver>
/*
Set to YES, then call -[SPUUpdater checkForUpdates]. At the end of the update cycle it will be reset to NO.
Eg:
NSBundle *mainBundle = NSBundle.mainBundle;
AutoUserDriver *userDriver = [[AutoUserDriver alloc] initWithHostBundle:mainBundle delegate:nil];
SPUUpdater *updater = [[SPUUpdater alloc] initWithHostBundle:mainBundle applicationBundle:mainBundle userDriver:userDriver delegate:nil];
NSError *error = nil;
BOOL startedUpdater = [updater startUpdater:&error];
//...
// Make sure to set this property right before calling checkForUpdates
userDriver.automaticallyInstallsUpdatesForNextCycle = YES;
[updater checkForUpdates];
*/
@property (nonatomic) BOOL automaticallyInstallsUpdatesForNextCycle;
@end
// Fragile hack because it's private method on SPUStandardUserDriver we're relying on
@interface SPUStandardUserDriver (Private)
- (void)closeCheckingWindow;
@end
@implementation AutoUserDriver
{
BOOL _automaticallyInstallsUpdatesForCurrentCycle;
}
@synthesize automaticallyInstallsUpdatesForNextCycle = _automaticallyInstallsUpdatesForNextCycle;
- (void)showUpdateFoundWithAppcastItem:(SUAppcastItem *)appcastItem state:(SPUUserUpdateState *)state reply:(void (^)(SPUUserUpdateChoice))reply
{
// Can't automatically install updates that are info only.
// Also what to do if appcastItem is a criticalUpdate or majorUpgrade?
_automaticallyInstallsUpdatesForCurrentCycle = (state.userInitiated && _automaticallyInstallsUpdatesForNextCycle && !appcastItem.informationOnlyUpdate);
if (!_automaticallyInstallsUpdatesForCurrentCycle) {
[super showUpdateFoundWithAppcastItem:appcastItem state:state reply:reply];
} else {
// Hack
if ([self respondsToSelector:@selector(closeCheckingWindow)]) {
[self closeCheckingWindow];
}
reply(SPUUserUpdateChoiceInstall);
}
}
- (void)showUpdateNotFoundWithError:(NSError *)error acknowledgement:(void (^)(void))acknowledgement
{
_automaticallyInstallsUpdatesForCurrentCycle = _automaticallyInstallsUpdatesForNextCycle;
if (_automaticallyInstallsUpdatesForCurrentCycle) {
acknowledgement();
} else {
[super showUpdateNotFoundWithError:error acknowledgement:acknowledgement];
}
}
- (void)showReadyToInstallAndRelaunch:(void (^)(SPUUserUpdateChoice))reply
{
if (_automaticallyInstallsUpdatesForCurrentCycle) {
reply(SPUUserUpdateChoiceInstall);
} else {
[super showReadyToInstallAndRelaunch:reply];
}
}
- (void)dismissUpdateInstallation
{
// Reset state
_automaticallyInstallsUpdatesForCurrentCycle = NO;
_automaticallyInstallsUpdatesForNextCycle = NO;
[super dismissUpdateInstallation];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment