Skip to content

Commit 15f7dd5

Browse files
authored
Add allowsAutomaticUpdates property (#2809)
This allows us to determine if automatic downloading/installing of updates option (typically a checkbox) should be enabled or not. This property is based on whether or not automatically checking of updates is enabled, or the SUAllowsAutomaticUpdates option (if specified by developer for custom needs). The update alert now updates hiding/unhiding the automatic downloading checkbox if the automatic update checking option changes. The test application is now updated to use -[SPUUpdater allowsAutomaticUpdates] for enabling/disabling the automatic downloads update checkbox.
1 parent 8c2ff58 commit 15f7dd5

File tree

7 files changed

+161
-60
lines changed

7 files changed

+161
-60
lines changed

Sparkle/SPUUpdater.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,18 @@ SU_EXPORT @interface SPUUpdater : NSObject
251251
*/
252252
@property (nonatomic) BOOL automaticallyDownloadsUpdates;
253253

254+
/**
255+
A property indicating whether or not the *option* to automatically download updates in the background can be turned on.
256+
257+
This property can be used to determine whether an option to automatically download/install updates should be enabled.
258+
259+
Its value depends on `automaticallyChecksForUpdates`, or the `SUAllowsAutomaticUpdates`in the host bundle's Info.plist if specified.
260+
Don't set `SUAllowsAutomaticUpdates` in the Info.plist unless you need custom behavior.
261+
262+
This property is KVO compliant. This property must be called on the main thread.
263+
*/
264+
@property (nonatomic, readonly) BOOL allowsAutomaticUpdates;
265+
254266
/**
255267
The URL of the appcast used to download update information.
256268

Sparkle/SPUUpdater.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,25 @@ + (BOOL)automaticallyNotifiesObserversOfAutomaticallyDownloadsUpdates
10561056
return NO;
10571057
}
10581058

1059+
- (BOOL)allowsAutomaticUpdates
1060+
{
1061+
if (![NSThread isMainThread]) {
1062+
SULog(SULogLevelError, @"Error: -[SPUUpdater allowsAutomaticUpdates] must be called on the main thread.");
1063+
}
1064+
1065+
return [_updaterSettings allowsAutomaticUpdates];
1066+
}
1067+
1068+
+ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsAutomaticUpdates
1069+
{
1070+
return [NSSet setWithObject:@"updaterSettings.allowsAutomaticUpdates"];
1071+
}
1072+
1073+
+ (BOOL)automaticallyNotifiesObserversOfAllowsAutomaticUpdates
1074+
{
1075+
return NO;
1076+
}
1077+
10591078
- (void)setFeedURL:(NSURL * _Nullable)feedURL
10601079
{
10611080
if (![NSThread isMainThread]) {

Sparkle/SPUUpdaterSettings.h

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,11 @@ SU_EXPORT @interface SPUUpdaterSettings : NSObject
4949
@property (nonatomic) NSTimeInterval updateCheckInterval;
5050

5151
/**
52-
* The impatient update check interval.
52+
* Indicates whether or not automatically downloading updates is allowed to be turned on by the user.
5353
*
54-
* If an update has already been downloaded automatically in the background, Sparkle may not notify users of the update immediately,
55-
* and tries to install the update siliently on quit without notifying the user.
54+
* This property is determined by checking `automaticallyChecksForUpdates` and `allowsAutomaticUpdatesOption`.
5655
*
57-
* Sparkle uses this long impatient update check interval to decide when to notify the user of the update if they haven't quit the app for a long time.
58-
* By default this check interval is set to 604800 seconds (which is 1 week). This interval must be bigger than the `updateCheckInterval`.
59-
*/
60-
@property (nonatomic, readonly) NSTimeInterval impatientUpdateCheckInterval;
61-
62-
/**
63-
* Indicates whether or not automatically downloading updates is allowed to be turned on by the user.
64-
* If this value is nil, the developer has not explicitly specified this option.
65-
*/
66-
@property (readonly, nonatomic, nullable) NSNumber *allowsAutomaticUpdatesOption;
67-
68-
/**
69-
* Indicates whether or not automatically downloading updates is allowed to be turned on by the user.
56+
* This property is KVO compliant. This property must be called on the main thread.
7057
*/
7158
@property (readonly, nonatomic) BOOL allowsAutomaticUpdates;
7259

@@ -80,6 +67,25 @@ SU_EXPORT @interface SPUUpdaterSettings : NSObject
8067
*/
8168
@property (nonatomic) BOOL automaticallyDownloadsUpdates;
8269

70+
/**
71+
* Indicates whether or not the developer allows turning on updates being automatically downloaded and installed.
72+
* If this value is nil, the developer has not explicitly specified this option (which is the default).
73+
*
74+
* Please prefer to use `allowsAutomaticUpdates` instead.
75+
*/
76+
@property (readonly, nonatomic, nullable) NSNumber *allowsAutomaticUpdatesOption;
77+
78+
/**
79+
* The impatient update check interval.
80+
*
81+
* If an update has already been downloaded automatically in the background, Sparkle may not notify users of the update immediately,
82+
* and tries to install the update siliently on quit without notifying the user.
83+
*
84+
* Sparkle uses this long impatient update check interval to decide when to notify the user of the update if they haven't quit the app for a long time.
85+
* By default this check interval is set to 604800 seconds (which is 1 week). This interval must be bigger than the `updateCheckInterval`.
86+
*/
87+
@property (nonatomic, readonly) NSTimeInterval impatientUpdateCheckInterval;
88+
8389
/**
8490
* Indicates whether or not anonymous system profile information is sent when checking for updates.
8591
*

Sparkle/SPUUpdaterSettings.m

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
static NSString *SUImpatientUpdateCheckIntervalKeyPath = @"impatientUpdateCheckInterval";
2020
static NSString *SUAutomaticallyDownloadsUpdatesKeyPath = @"automaticallyDownloadsUpdates";
2121
static NSString *SUSendsSystemProfileKeyPath = @"sendsSystemProfile";
22+
static NSString *SUAllowsAutomaticUpdatesOptionKeyPath = @"allowsAutomaticUpdatesOption";
23+
static NSString *SUAllowsAutomaticUpdatesKeyPath = @"allowsAutomaticUpdates";
2224

2325
@implementation SPUUpdaterSettings
2426
{
@@ -34,6 +36,8 @@ @implementation SPUUpdaterSettings
3436
@synthesize impatientUpdateCheckInterval = _impatientUpdateCheckInterval;
3537
@synthesize automaticallyDownloadsUpdates = _automaticallyDownloadsUpdates;
3638
@synthesize sendsSystemProfile = _sendsSystemProfile;
39+
@synthesize allowsAutomaticUpdatesOption = _allowsAutomaticUpdatesOption;
40+
@synthesize allowsAutomaticUpdates = _allowsAutomaticUpdates;
3741

3842
- (instancetype)initWithHostBundle:(NSBundle *)hostBundle
3943
{
@@ -50,6 +54,8 @@ - (instancetype)initWithHostBundle:(NSBundle *)hostBundle
5054
_automaticallyChecksForUpdates = [self currentAutomaticallyChecksForUpdates];
5155
_updateCheckInterval = [self currentUpdateCheckInterval];
5256
_impatientUpdateCheckInterval = [self currentImpatientUpdateCheckInterval];
57+
_allowsAutomaticUpdatesOption = [self currentAllowsAutomaticUpdatesOption];
58+
_allowsAutomaticUpdates = [self currentAllowsAutomaticUpdates];
5359
_automaticallyDownloadsUpdates = [self currentAutomaticallyDownloadsUpdates];
5460
_sendsSystemProfile = [self currentSendsSystemProfile];
5561

@@ -93,6 +99,9 @@ - (void)processCurrentAutomaticallyChecksForUpdates SPU_OBJC_DIRECT
9399
_automaticallyChecksForUpdates = currentValue;
94100

95101
[self didChangeValueForKey:updatedKeyPath];
102+
103+
[self processAllowsAutomaticUpdates];
104+
[self processAutomaticallyDownloadsUpdates];
96105
}
97106
}
98107

@@ -126,6 +135,36 @@ - (void)processImpatientUpdateCheckInterval SPU_OBJC_DIRECT
126135
}
127136
}
128137

138+
- (void)processAllowsAutomaticUpdatesOption SPU_OBJC_DIRECT
139+
{
140+
NSNumber *currentValue = [self currentAllowsAutomaticUpdatesOption];
141+
142+
if (((currentValue != nil) != (_allowsAutomaticUpdatesOption != nil)) || (currentValue.boolValue != _allowsAutomaticUpdatesOption.boolValue)) {
143+
NSString *updatedKeyPath = SUAllowsAutomaticUpdatesOptionKeyPath;
144+
145+
[self willChangeValueForKey:updatedKeyPath];
146+
147+
_allowsAutomaticUpdatesOption = currentValue;
148+
149+
[self didChangeValueForKey:updatedKeyPath];
150+
}
151+
}
152+
153+
- (void)processAllowsAutomaticUpdates SPU_OBJC_DIRECT
154+
{
155+
BOOL currentValue = [self currentAllowsAutomaticUpdates];
156+
157+
if (currentValue != _allowsAutomaticUpdates) {
158+
NSString *updatedKeyPath = SUAllowsAutomaticUpdatesKeyPath;
159+
160+
[self willChangeValueForKey:updatedKeyPath];
161+
162+
_allowsAutomaticUpdates = currentValue;
163+
164+
[self didChangeValueForKey:updatedKeyPath];
165+
}
166+
}
167+
129168
- (void)processAutomaticallyDownloadsUpdates SPU_OBJC_DIRECT
130169
{
131170
BOOL currentValue = [self currentAutomaticallyDownloadsUpdates];
@@ -172,6 +211,8 @@ - (void)synchronize:(NSNotification *)notification
172211
[self processCurrentAutomaticallyChecksForUpdates];
173212
[self processUpdateCheckInterval];
174213
[self processImpatientUpdateCheckInterval];
214+
[self processAllowsAutomaticUpdatesOption];
215+
[self processAllowsAutomaticUpdates];
175216
[self processAutomaticallyDownloadsUpdates];
176217
[self processSendsSystemProfile];
177218
}
@@ -201,6 +242,9 @@ - (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyCheckForUpdates
201242
} else {
202243
[NSNotificationCenter.defaultCenter postNotificationName:SUUpdateAutomaticCheckSettingChangedNotification object:nil userInfo:@{SUUpdateBundlePathUserInfoKey: _host.bundlePath}];
203244
}
245+
246+
[self processAllowsAutomaticUpdates];
247+
[self processAutomaticallyDownloadsUpdates];
204248
}
205249

206250
+ (BOOL)automaticallyNotifiesObserversOfAutomaticallyChecksForUpdates
@@ -255,22 +299,32 @@ + (BOOL)automaticallyNotifiesObserversOfImpatientUpdateCheckInterval
255299
return NO;
256300
}
257301

258-
// For allowing automatic downloaded updates to be turned on or off
259-
- (NSNumber * _Nullable)allowsAutomaticUpdatesOption
302+
- (NSNumber * _Nullable)currentAllowsAutomaticUpdatesOption SPU_OBJC_DIRECT
260303
{
261304
NSNumber *developerAllowsAutomaticUpdates = [_host boolNumberForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey];
262305
return developerAllowsAutomaticUpdates;
263306
}
264307

265-
- (BOOL)allowsAutomaticUpdates
308+
+ (BOOL)automaticallyNotifiesObserversOfAllowsAutomaticUpdatesOption
266309
{
267-
NSNumber *developerAllowsAutomaticUpdates = [self allowsAutomaticUpdatesOption];
268-
return (developerAllowsAutomaticUpdates == nil || developerAllowsAutomaticUpdates.boolValue);
310+
return NO;
311+
}
312+
313+
// This depends on currentAllowsAutomaticUpdatesOption and currentAutomaticallyChecksForUpdates and must be processed afterwards
314+
- (BOOL)currentAllowsAutomaticUpdates
315+
{
316+
return (_allowsAutomaticUpdatesOption == nil) ? _automaticallyChecksForUpdates : _allowsAutomaticUpdatesOption.boolValue;
317+
}
318+
319+
+ (BOOL)automaticallyNotifiesObserversOfAllowsAutomaticUpdates
320+
{
321+
return NO;
269322
}
270323

324+
// This depends on currentAllowsAutomaticUpdates and must be processed afterwards
271325
- (BOOL)currentAutomaticallyDownloadsUpdates SPU_OBJC_DIRECT
272326
{
273-
return [self allowsAutomaticUpdates] && [_host boolForKey:SUAutomaticallyUpdateKey];
327+
return _allowsAutomaticUpdates && [_host boolForKey:SUAutomaticallyUpdateKey];
274328
}
275329

276330
- (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyDownloadsUpdates

Sparkle/SUUpdateAlert.m

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#import "SPUUserUpdateState.h"
3333

3434
static NSString *const SUUpdateAlertTouchBarIdentifier = @"" SPARKLE_BUNDLE_IDENTIFIER ".SUUpdateAlert";
35+
static NSString *const SUAllowsAutomaticUpdatesKeyPath = @"allowsAutomaticUpdates";
3536

3637
static const CGFloat SUUpdateAlertGroupElementSpacing = 12.0;
3738

@@ -60,7 +61,6 @@ @implementation SUUpdateAlert
6061
void (^_didBecomeKeyBlock)(void);
6162
void(^_completionBlock)(SPUUserUpdateChoice, NSRect, BOOL);
6263

63-
BOOL _allowsAutomaticUpdates;
6464
BOOL _windowLoadedAndShowsReleaseNotes;
6565
}
6666

@@ -78,24 +78,20 @@ - (instancetype)initWithAppcastItem:(SUAppcastItem *)item state:(SPUUserUpdateSt
7878

7979
_updaterSettings = updaterSettings;
8080

81-
BOOL allowsAutomaticUpdates;
82-
NSNumber *allowsAutomaticUpdatesOption = _updaterSettings.allowsAutomaticUpdatesOption;
83-
if (item.informationOnlyUpdate) {
84-
allowsAutomaticUpdates = NO;
85-
} else if (allowsAutomaticUpdatesOption == nil) {
86-
allowsAutomaticUpdates = _updaterSettings.automaticallyChecksForUpdates;
87-
} else {
88-
allowsAutomaticUpdates = allowsAutomaticUpdatesOption.boolValue;
89-
}
90-
_allowsAutomaticUpdates = allowsAutomaticUpdates;
91-
9281
[self setShouldCascadeWindows:NO];
9382
} else {
9483
assert(false);
9584
}
9685
return self;
9786
}
9887

88+
- (void)dealloc
89+
{
90+
if (self.windowLoaded) {
91+
[_updaterSettings removeObserver:self forKeyPath:SUAllowsAutomaticUpdatesKeyPath];
92+
}
93+
}
94+
9995
- (NSString *)description
10096
{
10197
return [NSString stringWithFormat:@"%@ <%@>", [self class], _host.bundlePath];
@@ -338,6 +334,15 @@ - (void)_createReleaseNotesViewPreferringPlainText:(BOOL)preferringPlainText SPU
338334
}
339335
}
340336

337+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
338+
{
339+
if ([keyPath isEqualToString:SUAllowsAutomaticUpdatesKeyPath]) {
340+
_automaticallyInstallUpdatesButton.superview.hidden = !_updaterSettings.allowsAutomaticUpdates;
341+
} else {
342+
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
343+
}
344+
}
345+
341346
- (void)windowDidLoad
342347
{
343348
NSWindow *window = self.window;
@@ -395,8 +400,6 @@ - (void)windowDidLoad
395400
[_installButton setTitle:SULocalizedStringFromTableInBundle(@"Learn More…", SPARKLE_TABLE, sparkleBundle, @"Alternate title for 'Install Update' button when there's no download in RSS feed.")];
396401
[_installButton setAction:@selector(openInfoURL:)];
397402
}
398-
399-
BOOL allowsAutomaticUpdates = _allowsAutomaticUpdates;
400403

401404
if (showReleaseNotes) {
402405
[self displayReleaseNotesSpinner];
@@ -409,9 +412,7 @@ - (void)windowDidLoad
409412

410413
// NOTE: The code below for deciding what buttons to hide is complex! Due to array of feature configurations :)
411414

412-
if (!allowsAutomaticUpdates) {
413-
_automaticallyInstallUpdatesButton.superview.hidden = YES;
414-
}
415+
[_updaterSettings addObserver:self forKeyPath:SUAllowsAutomaticUpdatesKeyPath options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:NULL];
415416

416417
if (_state.stage == SPUUserUpdateStageInstalling) {
417418
// We're going to be relaunching pretty instantaneously

0 commit comments

Comments
 (0)