Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Beginnings of insane SUHost-based refactoring to get rid of NSBundle+…

…Sparkle. More super-unstable refactorings to come...
  • Loading branch information...
commit 98832c49145411c5e13e118fe686ff6c52bcbbe6 1 parent f4c6738
Andy Matuschak andymatuschak authored
Showing with 526 additions and 546 deletions.
  1. +0 −59 NSBundle+SUAdditions.h
  2. +0 −89 NSBundle+SUAdditions.m
  3. +3 −3 SUAutomaticUpdateAlert.h
  4. +7 −7 SUAutomaticUpdateAlert.m
  5. +4 −4 SUAutomaticUpdateDriver.m
  6. +4 −5 SUBasicUpdateDriver.h
  7. +35 −36 SUBasicUpdateDriver.m
  8. +32 −0 SUHost.h
  9. +169 −0 SUHost.m
  10. +5 −4 SUInstaller.h
  11. +14 −14 SUInstaller.m
  12. +1 −1  SUPackageInstaller.h
  13. +2 −2 SUPackageInstaller.m
  14. +1 −1  SUPlainInstaller.h
  15. +6 −6 SUPlainInstaller.m
  16. +3 −2 SUStatusController.h
  17. +6 −6 SUStatusController.m
  18. +1 −1  SUSystemProfiler.h
  19. +3 −3 SUSystemProfiler.m
  20. +12 −12 SUUIBasedUpdateDriver.m
  21. +3 −3 SUUpdateAlert.h
  22. +11 −11 SUUpdateAlert.m
  23. +4 −1 SUUpdateDriver.h
  24. +2 −2 SUUpdateDriver.m
  25. +3 −2 SUUpdatePermissionPrompt.h
  26. +11 −11 SUUpdatePermissionPrompt.m
  27. +12 −15 SUUpdater.h
  28. +156 −84 SUUpdater.m
  29. +0 −41 SUUserDefaults.h
  30. +0 −97 SUUserDefaults.m
  31. +3 −3 SUUserInitiatedUpdateDriver.m
  32. +2 −1  SUWindowController.h
  33. +1 −1  SUWindowController.m
  34. +1 −2  Sparkle.h
  35. +9 −17 Sparkle.xcodeproj/project.pbxproj
59 NSBundle+SUAdditions.h
View
@@ -1,59 +0,0 @@
-//
-// NSBundle+SUAdditions.h
-// Sparkle
-//
-// Created by Andy Matuschak on 12/21/07.
-// Copyright 2007 Andy Matuschak. All rights reserved.
-//
-
-#ifndef NSBUNDLE_PLUS_ADDITIONS_H
-#define NSBUNDLE_PLUS_ADDITIONS_H
-
-#import <Cocoa/Cocoa.h>
-
-@interface NSBundle (SUAdditions)
-/*!
- @method
- @abstract Returns a name for the bundle suitable for display to the user.
- @discussion This is performed by asking NSFileManager for the display name of the bundle.
-*/
-- (NSString *)name;
-
-/*!
- @method
- @abstract Returns the current internal version of the bundle.
- @discussion This uses the CFBundleVersion info value. This string is not appropriate for display to users: use -displayVersion instead.
-*/
-- (NSString *)version;
-
-/*!
- @method
- @abstract Returns the bundle's version, suitable for display to the user.
- @discussion If the CFBundleShortVersionString is available and different from the CFBundleVersion, this looks like CFBundleShortVersionString (CFBundleVersion). If the version strings are the same or CFBundleShortVersionString is not defined, this is equivalent to -version.
-*/
-- (NSString *)displayVersion;
-
-/*!
- @method
- @abstract Returns a suitable icon for this bundle.
- @discussion Uses the CFBundleIconFile icon if defined; otherwise, uses the default application icon.
-*/
-- (NSImage *)icon;
-
-/*!
- @method
- @abstract Returns whether the application is running from a disk image.
-*/
-- (BOOL)isRunningFromDiskImage;
-
-/*!
- @method
- @abstract Returns a profile of the users system useful for statistical purposes.
- @discussion Returns an array of dictionaries; each dictionary represents a piece of data and has keys "key", "visibleKey", "value", and "visibleValue".
-*/
-- (NSArray *)systemProfile;
-
-- (NSString *)publicDSAKey;
-@end
-
-#endif
89 NSBundle+SUAdditions.m
View
@@ -1,89 +0,0 @@
-//
-// NSBundle+SUAdditions.m
-// Sparkle
-//
-// Created by Andy Matuschak on 12/21/07.
-// Copyright 2007 Andy Matuschak. All rights reserved.
-//
-
-#import "Sparkle.h"
-#import "NSBundle+SUAdditions.h"
-
-#ifndef NSAppKitVersionNumber10_4
-#define NSAppKitVersionNumber10_4 824
-#endif
-
-@implementation NSBundle (SUAdditions)
-
-- (NSString *)name
-{
- NSString *name = [self objectForInfoDictionaryKey:@"CFBundleDisplayName"];
- if (name) return name;
-
- name = [self objectForInfoDictionaryKey:@"CFBundleName"];
- if (name) return name;
-
- return [[[NSFileManager defaultManager] displayNameAtPath:[self bundlePath]] stringByDeletingPathExtension];
-}
-
-- (NSString *)version
-{
- return [self objectForInfoDictionaryKey:@"CFBundleVersion"];
-}
-
-- (NSString *)displayVersion
-{
- NSString *shortVersionString = [self objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- if (shortVersionString)
- return shortVersionString;
- else
- return [self version]; // Fall back on the normal version string.
-}
-
-- (NSImage *)icon
-{
- // Cache the application icon.
- NSString *iconPath = [self pathForResource:[self objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:@"icns"];
- // According to the OS X docs, "CFBundleIconFile - This key identifies the file containing
- // the icon for the bundle. The filename you specify does not need to include the .icns
- // extension, although it may."
- //
- // However, if it *does* include the '.icns' the above method fails (tested on OS X 10.3.9) so we'll also try:
- if (!iconPath)
- iconPath = [self pathForResource:[self objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType: nil];
- NSImage *icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
- // Use a default icon if none is defined.
- if (!icon) { icon = [NSImage imageNamed:@"NSDefaultApplicationIcon"]; }
- return icon;
-}
-
-- (BOOL)isRunningFromDiskImage
-{
- // This check causes crashes on 10.3; for now, we'll just skip it.
- if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_4)
- return NO;
-
- NSDictionary *pathProperties = [[NSWorkspace sharedWorkspace] propertiesForPath:[self bundlePath]];
- BOOL isDiskImage = [pathProperties objectForKey:NSWorkspace_RBimagefilepath] != nil;
- BOOL isFileVault = [[pathProperties objectForKey:NSWorkspace_RBmntonname] hasPrefix:@"/Users/"];
- return isDiskImage && !isFileVault;
-}
-
-- (NSString *)publicDSAKey
-{
- // Maybe the key is just a string in the Info.plist.
- NSString *key = [self objectForInfoDictionaryKey:SUPublicDSAKeyKey];
- if (key) { return key; }
-
- // More likely, we've got a reference to a Resources file by filename:
- NSString *keyFilename = [self objectForInfoDictionaryKey:SUPublicDSAKeyFileKey];
- if (!keyFilename) { return nil; }
- return [NSString stringWithContentsOfFile:[self pathForResource:keyFilename ofType:nil]];
-}
-
-- (NSArray *)systemProfile
-{
- return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHostBundle:self];
-}
-
-@end
6 SUAutomaticUpdateAlert.h
View
@@ -18,14 +18,14 @@ typedef enum
SUDoNotInstallChoice
} SUAutomaticInstallationChoice;
-@class SUAppcastItem;
+@class SUAppcastItem, SUHost;
@interface SUAutomaticUpdateAlert : SUWindowController {
SUAppcastItem *updateItem;
id delegate;
- NSBundle *hostBundle;
+ SUHost *host;
}
-- (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle delegate:delegate;
+- (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)hostBundle delegate:delegate;
- (IBAction)installNow:sender;
- (IBAction)installLater:sender;
- (IBAction)doNotInstall:sender;
14 SUAutomaticUpdateAlert.m
View
@@ -11,14 +11,14 @@
@implementation SUAutomaticUpdateAlert
-- (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb delegate:del;
+- (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)hb delegate:del;
{
- self = [super initWithHostBundle:hb windowNibName:@"SUAutomaticUpdateAlert"];
+ self = [super initWithHost:hb windowNibName:@"SUAutomaticUpdateAlert"];
if (self)
{
updateItem = [item retain];
delegate = del;
- hostBundle = [hb retain];
+ host = [hb retain];
[self setShouldCascadeWindows:NO];
[[self window] center];
}
@@ -27,7 +27,7 @@ - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb delega
- (void)dealloc
{
- [hostBundle release];
+ [host release];
[updateItem release];
[super dealloc];
}
@@ -53,17 +53,17 @@ - (IBAction)doNotInstall:sender
- (NSImage *)applicationIcon
{
- return [hostBundle icon];
+ return [host icon];
}
- (NSString *)titleText
{
- return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is ready to install!", nil), [hostBundle name]];
+ return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is ready to install!", nil), [host name]];
}
- (NSString *)descriptionText
{
- return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?", nil), [hostBundle name], [hostBundle displayVersion]];
+ return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?", nil), [host name], [host displayVersion]];
}
@end
8 SUAutomaticUpdateDriver.m
View
@@ -13,7 +13,7 @@ @implementation SUAutomaticUpdateDriver
- (void)unarchiverDidFinish:(SUUnarchiver *)ua
{
- alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:updateItem hostBundle:hostBundle delegate:self];
+ alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:updateItem host:host delegate:self];
if ([NSApp isActive])
[[alert window] makeKeyAndOrderFront:self];
else
@@ -40,7 +40,7 @@ - (void)automaticUpdateAlert:(SUAutomaticUpdateAlert *)aua finishedWithChoice:(S
break;
case SUDoNotInstallChoice:
- [[SUUserDefaults standardUserDefaults] setObject:[updateItem versionString] forKey:SUSkippedVersionKey];
+ [host setObject:[updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey];
[self abortUpdate];
break;
}
@@ -59,9 +59,9 @@ - (void)applicationWillTerminate:(NSNotification *)note
[self installUpdate];
}
-- (void)installerFinishedForHostBundle:(NSBundle *)hb
+- (void)installerFinishedForHost:(SUHost *)aHost
{
- if (hb != hostBundle) { return; }
+ if (aHost != host) { return; }
if (!postponingInstallation)
[self relaunchHostApp];
}
9 SUBasicUpdateDriver.h
View
@@ -12,9 +12,8 @@
#import <Cocoa/Cocoa.h>
#import "SUUpdateDriver.h"
-@class SUAppcastItem, SUUnarchiver, SUAppcast, SUUnarchiver;
+@class SUAppcastItem, SUUnarchiver, SUAppcast, SUUnarchiver, SUHost;
@interface SUBasicUpdateDriver : SUUpdateDriver {
- NSBundle *hostBundle;
SUAppcastItem *updateItem;
NSURLDownload *download;
@@ -23,7 +22,7 @@
NSString *relaunchPath;
}
-- (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb;
+- (void)checkForUpdatesAtURL:(NSURL *)appcastURL host:(SUHost *)hb;
- (void)appcastDidFinishLoading:(SUAppcast *)ac;
- (void)appcast:(SUAppcast *)ac failedToLoadWithError:(NSError *)error;
@@ -45,8 +44,8 @@
- (void)unarchiverDidFail:(SUUnarchiver *)ua;
- (void)installUpdate;
-- (void)installerFinishedForHostBundle:(NSBundle *)hb;
-- (void)installerForHostBundle:(NSBundle *)hb failedWithError:(NSError *)error;
+- (void)installerFinishedForHost:(SUHost *)hb;
+- (void)installerForHost:(SUHost *)hb failedWithError:(NSError *)error;
- (void)relaunchHostApp;
- (void)cleanUp;
71 SUBasicUpdateDriver.m
View
@@ -6,18 +6,17 @@
// Copyright 2008 Andy Matuschak. All rights reserved.
//
-#import "SUBasicUpdateDriver.h"
#import "Sparkle.h"
+#import "SUBasicUpdateDriver.h"
@implementation SUBasicUpdateDriver
-- (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
+- (void)checkForUpdatesAtURL:(NSURL *)appcastURL host:(SUHost *)aHost
{
- hostBundle = [hb retain];
-
- if ([hostBundle isRunningFromDiskImage])
+ [super checkForUpdatesAtURL:appcastURL host:aHost];
+ if ([aHost isRunningFromDiskImage])
{
- [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURunningFromDiskImageError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a disk image. Move %1$@ to your Applications folder, relaunch it from there, and try again.", nil), [hostBundle name]] forKey:NSLocalizedDescriptionKey]]];
+ [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURunningFromDiskImageError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a disk image. Move %1$@ to your Applications folder, relaunch it from there, and try again.", nil), [aHost name]] forKey:NSLocalizedDescriptionKey]]];
return;
}
@@ -26,9 +25,9 @@ - (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
[appcast release];
[appcast setDelegate:self];
- [appcast setUserAgentString:[NSString stringWithFormat: @"%@/%@ Sparkle/1.5b4", [hostBundle name], [hostBundle displayVersion]]];
+ [appcast setUserAgentString:[NSString stringWithFormat: @"%@/%@ Sparkle/1.5b4", [aHost name], [aHost displayVersion]]];
[appcast fetchAppcastFromURL:appcastURL];
- [[SUUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:SULastCheckTimeKey];
+ [host setObject:[NSDate date] forUserDefaultsKey:SULastCheckTimeKey];
}
- (id <SUVersionComparison>)_versionComparator
@@ -36,8 +35,8 @@ - (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
id <SUVersionComparison> comparator = nil;
// Give the delegate a chance to provide a custom version comparator
- if ([delegate respondsToSelector:@selector(versionComparatorForHostBundle:)])
- comparator = [delegate versionComparatorForHostBundle:hostBundle];
+ if ([delegate respondsToSelector:@selector(versionComparatorForHost:)])
+ comparator = [delegate versionComparatorForHost:host];
// If we don't get a comparator from the delegate, use the default comparator
if (!comparator)
@@ -48,7 +47,7 @@ - (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
- (BOOL)isItemNewer:(SUAppcastItem *)ui
{
- return [[self _versionComparator] compareVersion:[hostBundle version] toVersion:[ui versionString]] == NSOrderedAscending;
+ return [[self _versionComparator] compareVersion:[host version] toVersion:[ui versionString]] == NSOrderedAscending;
}
- (BOOL)hostSupportsItem:(SUAppcastItem *)ui
@@ -59,7 +58,7 @@ - (BOOL)hostSupportsItem:(SUAppcastItem *)ui
- (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui
{
- NSString *skippedVersion = [[SUUserDefaults standardUserDefaults] objectForKey:SUSkippedVersionKey];
+ NSString *skippedVersion = [host objectForUserDefaultsKey:SUSkippedVersionKey];
if (skippedVersion == nil) { return NO; }
return [[self _versionComparator] compareVersion:[ui versionString] toVersion:skippedVersion] != NSOrderedDescending;
}
@@ -71,13 +70,13 @@ - (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui
- (void)appcastDidFinishLoading:(SUAppcast *)ac
{
- if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:forHostBundle:)])
- [delegate appcastDidFinishLoading:ac forHostBundle:hostBundle];
+ if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:forHost:)])
+ [delegate appcastDidFinishLoading:ac forHost:host];
// Now we have to find the best valid update in the appcast.
- if ([delegate respondsToSelector:@selector(bestValidUpdateInAppcast:forHostBundle:)]) // Does the delegate want to handle it?
+ if ([delegate respondsToSelector:@selector(bestValidUpdateInAppcast:forHost:)]) // Does the delegate want to handle it?
{
- updateItem = [delegate bestValidUpdateInAppcast:ac forHostBundle:hostBundle];
+ updateItem = [delegate bestValidUpdateInAppcast:ac forHost:host];
}
else // If not, we'll take care of it ourselves.
{
@@ -106,16 +105,16 @@ - (void)appcast:(SUAppcast *)ac failedToLoadWithError:(NSError *)error
- (void)didFindValidUpdate
{
- if ([delegate respondsToSelector:@selector(didFindValidUpdate:toHostBundle:)])
- [delegate didFindValidUpdate:updateItem toHostBundle:hostBundle];
+ if ([delegate respondsToSelector:@selector(didFindValidUpdate:toHost:)])
+ [delegate didFindValidUpdate:updateItem toHost:host];
[self downloadUpdate];
}
- (void)didNotFindUpdate
{
- if ([delegate respondsToSelector:@selector(didNotFindUpdateToHostBundle:)])
- [delegate didNotFindUpdateToHostBundle:hostBundle];
- [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUNoUpdateError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"You already have the newest version of %@.", nil), [hostBundle name]] forKey:NSLocalizedDescriptionKey]]];
+ if ([delegate respondsToSelector:@selector(didNotFindUpdateToHost:)])
+ [delegate didNotFindUpdateToHost:host];
+ [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUNoUpdateError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"You already have the newest version of %@.", nil), [host name]] forKey:NSLocalizedDescriptionKey]]];
}
- (void)downloadUpdate
@@ -131,7 +130,7 @@ - (void)download:(NSURLDownload *)d decideDestinationWithSuggestedFilename:(NSSt
// We create a temporary directory in /tmp and stick the file there.
// Not using a GUID here because hdiutil for some reason chokes on GUIDs. Too long? I really have no idea.
- NSString *prefix = [NSString stringWithFormat:@"%@ %@ Update", [hostBundle name], [hostBundle version]];
+ NSString *prefix = [NSString stringWithFormat:@"%@ %@ Update", [host name], [host version]];
NSString *tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:prefix];
int cnt=1;
while ([[NSFileManager defaultManager] fileExistsAtPath:tempDir] && cnt <= 999999)
@@ -164,9 +163,9 @@ - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
- (void)extractUpdate
{
// DSA verification, if activated by the developer
- if ([[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue])
+ if ([[host objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue])
{
- if (![[NSFileManager defaultManager] validatePath:downloadPath withEncodedDSASignature:[updateItem DSASignature] withPublicDSAKey:[hostBundle publicDSAKey]])
+ if (![[NSFileManager defaultManager] validatePath:downloadPath withEncodedDSASignature:[updateItem DSASignature] withPublicDSAKey:[host publicDSAKey]])
{
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUSignatureError userInfo:[NSDictionary dictionaryWithObject:@"The update is improperly signed." forKey:NSLocalizedDescriptionKey]]];
return;
@@ -201,8 +200,8 @@ - (BOOL)shouldInstallSynchronously { return NO; }
- (void)installUpdate
{
- if ([delegate respondsToSelector:@selector(updateWillInstall:toHostBundle:)])
- [delegate updateWillInstall:updateItem toHostBundle:hostBundle];
+ if ([delegate respondsToSelector:@selector(updateWillInstall:toHost:)])
+ [delegate updateWillInstall:updateItem toHost:host];
// Copy the relauncher into a temporary directory so we can get to it after the new version's installed.
NSString *relaunchPathToCopy = [[NSBundle bundleForClass:[self class]] pathForResource:@"relaunch" ofType:@""];
NSString *targetPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]];
@@ -211,12 +210,12 @@ - (void)installUpdate
if ([[NSFileManager defaultManager] copyPath:relaunchPathToCopy toPath:targetPath handler:nil])
relaunchPath = [targetPath retain];
- [SUInstaller installFromUpdateFolder:[downloadPath stringByDeletingLastPathComponent] overHostBundle:hostBundle delegate:self synchronously:[self shouldInstallSynchronously]];
+ [SUInstaller installFromUpdateFolder:[downloadPath stringByDeletingLastPathComponent] overHost:host delegate:self synchronously:[self shouldInstallSynchronously]];
}
-- (void)installerFinishedForHostBundle:(NSBundle *)hb
+- (void)installerFinishedForHost:(SUHost *)aHost
{
- if (hb != hostBundle) { return; }
+ if (aHost != host) { return; }
[self relaunchHostApp];
}
@@ -224,13 +223,13 @@ - (void)relaunchHostApp
{
// Give the host app an opportunity to postpone the relaunch.
static BOOL postponedOnce = NO;
- if (!postponedOnce && [delegate respondsToSelector:@selector(shouldPostponeRelaunchForUpdate:toHostBundle:untilInvoking:)])
+ if (!postponedOnce && [delegate respondsToSelector:@selector(shouldPostponeRelaunchForUpdate:toHost:untilInvoking:)])
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[[self class] instanceMethodSignatureForSelector:@selector(relaunchHostApp)] retain]];
[invocation setSelector:@selector(relaunchHostApp)];
[invocation setTarget:self];
postponedOnce = YES;
- if ([delegate shouldPostponeRelaunchForUpdate:updateItem toHostBundle:hostBundle untilInvoking:invocation])
+ if ([delegate shouldPostponeRelaunchForUpdate:updateItem toHost:host untilInvoking:invocation])
return;
}
@@ -243,12 +242,12 @@ - (void)relaunchHostApp
if(!relaunchPath || ![[NSFileManager defaultManager] fileExistsAtPath:relaunchPath])
{
// Note that we explicitly use the host app's name here, since updating plugin for Mail relaunches Mail, not just the plugin.
- [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [[NSBundle mainBundle] name]], NSLocalizedDescriptionKey, [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", relaunchPath], NSLocalizedFailureReasonErrorKey, nil]]];
+ [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [host name]], NSLocalizedDescriptionKey, [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", relaunchPath], NSLocalizedFailureReasonErrorKey, nil]]];
// We intentionally don't abandon the update here so that the host won't initiate another.
return;
}
- [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:[hostBundle bundlePath], [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], nil]];
+ [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:[host bundlePath], [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], nil]];
[NSApp terminate:self];
}
@@ -258,9 +257,9 @@ - (void)cleanUp
[[NSFileManager defaultManager] removeFileAtPath:[downloadPath stringByDeletingLastPathComponent] handler:nil];
}
-- (void)installerForHostBundle:(NSBundle *)hb failedWithError:(NSError *)error
+- (void)installerForHost:(SUHost *)aHost failedWithError:(NSError *)error
{
- if (hb != hostBundle) { return; }
+ if (aHost != host) { return; }
[[NSFileManager defaultManager] removeFileAtPath:relaunchPath handler:NULL]; // Clean up the copied relauncher.
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while installing the update. Please try again later.", nil), NSLocalizedDescriptionKey, [error localizedDescription], NSLocalizedFailureReasonErrorKey, nil]]];
}
@@ -284,7 +283,7 @@ - (void)abortUpdateWithError:(NSError *)error
- (void)dealloc
{
- [hostBundle release];
+ [host release];
[download release];
[downloadPath release];
[relaunchPath release];
32 SUHost.h
View
@@ -0,0 +1,32 @@
+//
+// SUHost.h
+// Sparkle
+//
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#import "Sparkle.h"
+
+@interface SUHost : NSObject
+{
+ NSBundle *bundle;
+}
+
+- (id)initWithBundle:(NSBundle *)aBundle;
+- (NSBundle *)bundle;
+- (NSString *)bundlePath;
+- (NSString *)name;
+- (NSString *)version;
+- (NSString *)displayVersion;
+- (NSImage *)icon;
+- (BOOL)isRunningFromDiskImage;
+- (NSString *)publicDSAKey;
+- (NSArray *)systemProfile;
+
+- (id)objectForInfoDictionaryKey:(NSString *)key;
+- (BOOL)boolForInfoDictionaryKey:(NSString *)key;
+- (id)objectForUserDefaultsKey:(NSString *)defaultName;
+- (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName;
+- (BOOL)boolForUserDefaultsKey:(NSString *)defaultName;
+- (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName;
+@end
169 SUHost.m
View
@@ -0,0 +1,169 @@
+//
+// SUHost.m
+// Sparkle
+//
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#import "Sparkle.h"
+
+@implementation SUHost
+
+- (id)initWithBundle:(NSBundle *)aBundle
+{
+ if (aBundle == nil) aBundle = [NSBundle mainBundle];
+ if ((self = [super init]))
+ {
+ bundle = [aBundle retain];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [bundle release];
+ [super dealloc];
+}
+
+- (NSBundle *)bundle
+{
+ return bundle;
+}
+
+- (NSString *)bundlePath
+{
+ return [bundle bundlePath];
+}
+
+- (NSString *)name
+{
+ NSString *name = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
+ if (name) return name;
+
+ name = [self objectForInfoDictionaryKey:@"CFBundleName"];
+ if (name) return name;
+
+ return [[[NSFileManager defaultManager] displayNameAtPath:[bundle bundlePath]] stringByDeletingPathExtension];
+}
+
+- (NSString *)version
+{
+ return [bundle objectForInfoDictionaryKey:@"CFBundleVersion"];
+}
+
+- (NSString *)displayVersion
+{
+ NSString *shortVersionString = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ if (shortVersionString)
+ return shortVersionString;
+ else
+ return [self version]; // Fall back on the normal version string.
+}
+
+- (NSImage *)icon
+{
+ // Cache the application icon.
+ NSString *iconPath = [bundle pathForResource:[bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:@"icns"];
+ // According to the OS X docs, "CFBundleIconFile - This key identifies the file containing
+ // the icon for the bundle. The filename you specify does not need to include the .icns
+ // extension, although it may."
+ //
+ // However, if it *does* include the '.icns' the above method fails (tested on OS X 10.3.9) so we'll also try:
+ if (!iconPath)
+ iconPath = [bundle pathForResource:[bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType: nil];
+ NSImage *icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
+ // Use a default icon if none is defined.
+ if (!icon) { icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)]; }
+ return icon;
+}
+
+- (BOOL)isRunningFromDiskImage
+{
+ // This check causes crashes on 10.3; for now, we'll just skip it.
+ if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_4)
+ return NO;
+
+ NSDictionary *pathProperties = [[NSWorkspace sharedWorkspace] propertiesForPath:[bundle bundlePath]];
+ BOOL isDiskImage = [pathProperties objectForKey:NSWorkspace_RBimagefilepath] != nil;
+ BOOL isFileVault = [[pathProperties objectForKey:NSWorkspace_RBmntonname] hasPrefix:@"/Users/"];
+ return isDiskImage && !isFileVault;
+}
+
+- (NSString *)publicDSAKey
+{
+ // Maybe the key is just a string in the Info.plist.
+ NSString *key = [bundle objectForInfoDictionaryKey:SUPublicDSAKeyKey];
+ if (key) { return key; }
+
+ // More likely, we've got a reference to a Resources file by filename:
+ NSString *keyFilename = [self objectForInfoDictionaryKey:SUPublicDSAKeyFileKey];
+ if (!keyFilename) { return nil; }
+ return [NSString stringWithContentsOfFile:[bundle pathForResource:keyFilename ofType:nil]];
+}
+
+- (NSArray *)systemProfile
+{
+ return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHost:self];
+}
+
+- (id)objectForInfoDictionaryKey:(NSString *)key
+{
+ return [bundle objectForInfoDictionaryKey:key];
+}
+
+- (BOOL)boolForInfoDictionaryKey:(NSString *)key
+{
+ return [[self objectForInfoDictionaryKey:key] boolValue];
+}
+
+- (id)objectForUserDefaultsKey:(NSString *)defaultName
+{
+ CFPropertyListRef obj = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]);
+ // Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistratioDomain, so anything
+ // passed into -[NSUserDefaults registerDefaults:] is ignored. The following line falls
+ // back to using NSUserDefaults, but only if the host bundle is the main bundle, and no value
+ // is found elsewhere.
+ if (obj == NULL && bundle != [NSBundle mainBundle])
+ obj = [[NSUserDefaults standardUserDefaults] objectForKey:defaultName];
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1050
+ return [NSMakeCollectable(obj) autorelease];
+#else
+ return [(id)obj autorelease];
+#endif
+}
+
+- (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName;
+{
+ CFPreferencesSetValue((CFStringRef)defaultName, value, (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ CFPreferencesSynchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ // If anything's bound to this through an NSUserDefaultsController, it won't know that anything's changed.
+ // We can't get an NSUserDefaults object for anything other than the standard one for the app, so this won't work for bundles.
+ // But it's the best we can do: this will make NSUserDefaultsControllers know about the changes that have been made.
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (BOOL)boolForUserDefaultsKey:(NSString *)defaultName
+{
+ BOOL value;
+ CFPropertyListRef plr = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]);
+ if (plr == NULL)
+ value = NO;
+ else {
+ value = (BOOL)CFBooleanGetValue((CFBooleanRef)plr);
+ CFRelease(plr);
+ }
+
+ return value;
+}
+
+- (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName
+{
+ CFPreferencesSetValue((CFStringRef)defaultName, (CFBooleanRef)[NSNumber numberWithBool:value], (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ CFPreferencesSynchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ // If anything's bound to this through an NSUserDefaultsController, it won't know that anything's changed.
+ // We can't get an NSUserDefaults object for anything other than the standard one for the app, so this won't work for bundles.
+ // But it's the best we can do: this will make NSUserDefaultsControllers know about the changes that have been made.
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+@end
9 SUInstaller.h
View
@@ -11,14 +11,15 @@
#import <Cocoa/Cocoa.h>
+@class SUHost;
@interface SUInstaller : NSObject { }
-+ (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously;
-+ (void)_finishInstallationWithResult:(BOOL)result hostBundle:(NSBundle *)hostBundle error:(NSError *)error delegate:delegate;
++ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
++ (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate;
@end
@interface NSObject (SUInstallerDelegateInformalProtocol)
-- (void)installerFinishedForHostBundle:(NSBundle *)hostBundle;
-- (void)installerForHostBundle:(NSBundle *)hostBundle failedWithError:(NSError *)error;
+- (void)installerFinishedForHost:(SUHost *)host;
+- (void)installerForHost:(SUHost *)host failedWithError:(NSError *)error;
@end
#endif
28 SUInstaller.m
View
@@ -11,15 +11,15 @@
#import "SUPackageInstaller.h"
NSString *SUInstallerPathKey = @"SUInstallerPath";
-NSString *SUInstallerHostBundleKey = @"SUInstallerHostBundle";
+NSString *SUInstallerHostKey = @"SUInstallerHost";
NSString *SUInstallerDelegateKey = @"SUInstallerDelegate";
@implementation SUInstaller
-+ (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously
++ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously
{
// Search subdirectories for the application
- NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[hostBundle bundlePath] lastPathComponent], *alternateBundleFileName = [[hostBundle name] stringByAppendingPathExtension:[[hostBundle bundlePath] pathExtension]];
+ NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[host bundlePath] lastPathComponent], *alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]];
BOOL isPackage = NO;
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:updateFolder];
while ((currentFile = [dirEnum nextObject]))
@@ -42,7 +42,7 @@ + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundl
// Some DMGs have symlinks into /Applications! That's no good! And there's no point in looking in bundles.
if ([[NSFileManager defaultManager] isAliasFolderAtPath:currentPath] ||
- [[currentFile pathExtension] isEqualToString:[[hostBundle bundlePath] pathExtension]] ||
+ [[currentFile pathExtension] isEqualToString:[[host bundlePath] pathExtension]] ||
[[currentFile pathExtension] isEqualToString:@"pkg"] ||
[[currentFile pathExtension] isEqualToString:@"mpkg"])
{
@@ -52,34 +52,34 @@ + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundl
if (newAppDownloadPath == nil)
{
- [self _finishInstallationWithResult:NO hostBundle:hostBundle error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find an appropriate update in the downloaded package." forKey:NSLocalizedDescriptionKey]] delegate:delegate];
+ [self _finishInstallationWithResult:NO host:host error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find an appropriate update in the downloaded package." forKey:NSLocalizedDescriptionKey]] delegate:delegate];
}
else
{
- [(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath hostBundle:hostBundle delegate:delegate synchronously:synchronously];
+ [(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously];
}
}
-+ (void)_mdimportBundle:(NSBundle *)bundle
++ (void)_mdimportHost:(SUHost *)host
{
NSTask *mdimport = [[[NSTask alloc] init] autorelease];
[mdimport setLaunchPath:@"/usr/bin/mdimport"];
- [mdimport setArguments:[NSArray arrayWithObject:[bundle bundlePath]]];
+ [mdimport setArguments:[NSArray arrayWithObject:[host bundlePath]]];
[mdimport launch];
}
-+ (void)_finishInstallationWithResult:(BOOL)result hostBundle:(NSBundle *)hostBundle error:(NSError *)error delegate:delegate
++ (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate
{
if (result == YES)
{
- [self _mdimportBundle:hostBundle];
- if ([delegate respondsToSelector:@selector(installerFinishedForHostBundle:)])
- [delegate installerFinishedForHostBundle:hostBundle];
+ [self _mdimportHost:host];
+ if ([delegate respondsToSelector:@selector(installerFinishedForHost:)])
+ [delegate installerFinishedForHost:host];
}
else
{
- if ([delegate respondsToSelector:@selector(installerForHostBundle:failedWithError:)])
- [delegate installerForHostBundle:hostBundle failedWithError:error];
+ if ([delegate respondsToSelector:@selector(installerForHost:failedWithError:)])
+ [delegate installerForHost:host failedWithError:error];
}
}
2  SUPackageInstaller.h
View
@@ -13,7 +13,7 @@
#import "SUPlainInstaller.h"
@interface SUPackageInstaller : SUPlainInstaller { }
-+ (void)installPath:(NSString *)path overHostBundle:(NSBundle *)bundle delegate:delegate;
++ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate;
@end
#endif
4 SUPackageInstaller.m
View
@@ -11,7 +11,7 @@
@implementation SUPackageInstaller
-+ (void)installPath:(NSString *)path overHostBundle:(NSBundle *)bundle delegate:delegate
++ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate
{
NSError *error = nil;
BOOL result = YES;
@@ -26,7 +26,7 @@ + (void)installPath:(NSString *)path overHostBundle:(NSBundle *)bundle delegate:
NSTask *installer = [NSTask launchedTaskWithLaunchPath:installerPath arguments:[NSArray arrayWithObjects:path, nil]];
[installer waitUntilExit];
// Known bug: if the installation fails or is canceled, Sparkle goes ahead and restarts, thinking everything is fine.
- [self _finishInstallationWithResult:result hostBundle:bundle error:error delegate:delegate];
+ [self _finishInstallationWithResult:result host:bundle error:error delegate:delegate];
}
@end
2  SUPlainInstaller.h
View
@@ -12,7 +12,7 @@
#import "Sparkle.h"
@interface SUPlainInstaller : SUInstaller { }
-+ (void)performInstallationWithPath:(NSString *)path hostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously;
++ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
@end
#endif
12 SUPlainInstaller.m
View
@@ -9,30 +9,30 @@
#import "SUPlainInstaller.h"
extern NSString *SUInstallerPathKey;
-extern NSString *SUInstallerHostBundleKey;
+extern NSString *SUInstallerHostKey;
extern NSString *SUInstallerDelegateKey;
@implementation SUPlainInstaller
-+ (void)installPath:(NSString *)path overHostBundle:(NSBundle *)bundle delegate:delegate
++ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate
{
NSError *error;
BOOL result = [[NSFileManager defaultManager] copyPathWithAuthentication:path overPath:[bundle bundlePath] error:&error];
- [self _finishInstallationWithResult:result hostBundle:bundle error:error delegate:delegate];
+ [self _finishInstallationWithResult:result host:bundle error:error delegate:delegate];
}
+ (void)_performInstallationWithInfo:(NSDictionary *)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [self installPath:[info objectForKey:SUInstallerPathKey] overHostBundle:[info objectForKey:SUInstallerHostBundleKey] delegate:[info objectForKey:SUInstallerDelegateKey]];
+ [self installPath:[info objectForKey:SUInstallerPathKey] overHost:[info objectForKey:SUInstallerHostKey] delegate:[info objectForKey:SUInstallerDelegateKey]];
[pool drain];
}
-+ (void)performInstallationWithPath:(NSString *)path hostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously;
++ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
{
- NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, SUInstallerPathKey, hostBundle, SUInstallerHostBundleKey, delegate, SUInstallerDelegateKey, nil];
+ NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, SUInstallerPathKey, host, SUInstallerHostKey, delegate, SUInstallerDelegateKey, nil];
if (synchronously)
[self _performInstallationWithInfo:info];
else
5 SUStatusController.h
View
@@ -11,15 +11,16 @@
#import "SUWindowController.h"
+@class SUHost;
@interface SUStatusController : SUWindowController {
double progressValue, maxProgressValue;
NSString *title, *statusText, *buttonTitle;
IBOutlet NSButton *actionButton;
IBOutlet NSProgressIndicator* progressBar;
- NSBundle *hostBundle;
+ SUHost *host;
}
-- (id)initWithHostBundle:(NSBundle *)hostBundle;
+- (id)initWithHost:(SUHost *)host;
// Pass 0 for the max progress value to get an indeterminate progress bar.
// Pass nil for the status text to not show it.
12 SUStatusController.m
View
@@ -11,12 +11,12 @@
@implementation SUStatusController
-- (id)initWithHostBundle:(NSBundle *)hb
+- (id)initWithHost:(SUHost *)hb
{
- self = [super initWithHostBundle:hb windowNibName:@"SUStatus"];
+ self = [super initWithHost:hb windowNibName:@"SUStatus"];
if (self)
{
- hostBundle = [hb retain];
+ host = [hb retain];
[self setShouldCascadeWindows:NO];
}
return self;
@@ -24,7 +24,7 @@ - (id)initWithHostBundle:(NSBundle *)hb
- (void)dealloc
{
- [hostBundle release];
+ [host release];
[title release];
[statusText release];
[buttonTitle release];
@@ -40,12 +40,12 @@ - (void)awakeFromNib
- (NSString *)windowTitle
{
- return [NSString stringWithFormat:SULocalizedString(@"Updating %@", nil), [hostBundle name]];
+ return [NSString stringWithFormat:SULocalizedString(@"Updating %@", nil), [host name]];
}
- (NSImage *)applicationIcon
{
- return [hostBundle icon];
+ return [host icon];
}
- (void)beginActionWithTitle:(NSString *)aTitle maxProgressValue:(double)aMaxProgressValue statusText:(NSString *)aStatusText
2  SUSystemProfiler.h
View
@@ -11,7 +11,7 @@
@interface SUSystemProfiler : NSObject {}
+ (SUSystemProfiler *)sharedSystemProfiler;
-- (NSMutableArray *)systemProfileArrayForHostBundle:(NSBundle *)hostBundle;
+- (NSMutableArray *)systemProfileArrayForHost:(SUHost *)host;
@end
#endif
6 SUSystemProfiler.m
View
@@ -27,7 +27,7 @@ - (NSDictionary *)modelTranslationTable
return [[[NSDictionary alloc] initWithContentsOfFile:path] autorelease];
}
-- (NSMutableArray *)systemProfileArrayForHostBundle:(NSBundle *)hostBundle
+- (NSMutableArray *)systemProfileArrayForHost:(SUHost *)host
{
NSDictionary *modelTranslation = [self modelTranslationTable];
@@ -115,10 +115,10 @@ - (NSMutableArray *)systemProfileArrayForHostBundle:(NSBundle *)hostBundle
[profileArray addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"lang",@"Preferred Language", [languages objectAtIndex:0], [languages objectAtIndex:0],nil] forKeys:profileDictKeys]];
// Application sending the request
- NSString *appName = [hostBundle name];
+ NSString *appName = [host name];
if (appName)
[profileArray addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"appName",@"Application Name", appName, appName,nil] forKeys:profileDictKeys]];
- NSString *appVersion = [hostBundle version];
+ NSString *appVersion = [host version];
if (appVersion)
[profileArray addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"appVersion",@"Application Version", appVersion, appVersion,nil] forKeys:profileDictKeys]];
24 SUUIBasedUpdateDriver.m
View
@@ -13,11 +13,11 @@ @implementation SUUIBasedUpdateDriver
- (void)didFindValidUpdate
{
- updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:updateItem hostBundle:hostBundle];
+ updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:updateItem host:host];
[updateAlert setDelegate:self];
// If the app is a menubar app or the like, we need to focus it first:
- if ([[hostBundle objectForInfoDictionaryKey:@"LSUIElement"] doubleValue]) { [NSApp activateIgnoringOtherApps:YES]; }
+ if ([[host objectForInfoDictionaryKey:@"LSUIElement"] doubleValue]) { [NSApp activateIgnoringOtherApps:YES]; }
// Only show the update alert if the app is active; otherwise, we'll wait until it is.
if ([NSApp isActive])
@@ -28,10 +28,10 @@ - (void)didFindValidUpdate
- (void)didNotFindUpdate
{
- if ([delegate respondsToSelector:@selector(didNotFindUpdateToHostBundle:)])
- [delegate didNotFindUpdateToHostBundle:hostBundle];
- NSAlert *alert = [NSAlert alertWithMessageText:SULocalizedString(@"You're up to date!", nil) defaultButton:SULocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [hostBundle name], [hostBundle displayVersion]];
- [alert setIcon:[hostBundle icon]];
+ if ([delegate respondsToSelector:@selector(didNotFindUpdateToHost:)])
+ [delegate didNotFindUpdateToHost:host];
+ NSAlert *alert = [NSAlert alertWithMessageText:SULocalizedString(@"You're up to date!", nil) defaultButton:SULocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [host name], [host displayVersion]];
+ [alert setIcon:[host icon]];
[alert runModal];
[self abortUpdate];
}
@@ -45,13 +45,13 @@ - (void)applicationDidBecomeActive:(NSNotification *)aNotification
- (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoice)choice
{
[updateAlert release]; updateAlert = nil;
- if ([delegate respondsToSelector:@selector(userChoseAction:forUpdate:toHostBundle:)])
- [delegate userChoseAction:choice forUpdate:updateItem toHostBundle:hostBundle];
- [[SUUserDefaults standardUserDefaults] setObject:nil forKey:SUSkippedVersionKey];
+ if ([delegate respondsToSelector:@selector(userChoseAction:forUpdate:toHost:)])
+ [delegate userChoseAction:choice forUpdate:updateItem toHost:host];
+ [host setObject:nil forUserDefaultsKey:SUSkippedVersionKey];
switch (choice)
{
case SUInstallUpdateChoice:
- statusController = [[SUStatusController alloc] initWithHostBundle:hostBundle];
+ statusController = [[SUStatusController alloc] initWithHost:host];
[statusController beginActionWithTitle:SULocalizedString(@"Downloading update\u2026", @"Take care not to overflow the status window.") maxProgressValue:0 statusText:nil];
[statusController setButtonTitle:SULocalizedString(@"Cancel", nil) target:self action:@selector(cancelDownload:) isDefault:NO];
[statusController showWindow:self];
@@ -59,7 +59,7 @@ - (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoi
break;
case SUSkipThisVersionChoice:
- [[SUUserDefaults standardUserDefaults] setObject:[updateItem versionString] forKey:SUSkippedVersionKey];
+ [host setObject:[updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey];
case SURemindMeLaterChoice:
[self abortUpdate];
break;
@@ -138,7 +138,7 @@ - (void)installUpdate
- (void)abortUpdateWithError:(NSError *)error
{
NSAlert *alert = [NSAlert alertWithMessageText:SULocalizedString(@"Update Error!", nil) defaultButton:SULocalizedString(@"Cancel Update", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:[error localizedDescription]];
- [alert setIcon:[hostBundle icon]];
+ [alert setIcon:[host icon]];
[alert runModal];
[super abortUpdateWithError:error];
}
6 SUUpdateAlert.h
View
@@ -18,10 +18,10 @@ typedef enum
SUSkipThisVersionChoice
} SUUpdateAlertChoice;
-@class WebView, SUAppcastItem;
+@class WebView, SUAppcastItem, SUHost;
@interface SUUpdateAlert : SUWindowController {
SUAppcastItem *updateItem;
- NSBundle *hostBundle;
+ SUHost *host;
id delegate;
IBOutlet WebView *releaseNotesView;
@@ -30,7 +30,7 @@ typedef enum
BOOL webViewFinishedLoading;
}
-- (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle;
+- (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)host;
- (void)setDelegate:delegate;
- (IBAction)installUpdate:sender;
22 SUUpdateAlert.m
View
@@ -13,12 +13,12 @@
@implementation SUUpdateAlert
-- (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb
+- (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)hb
{
- self = [super initWithHostBundle:hb windowNibName:@"SUUpdateAlert"];
+ self = [super initWithHost:hb windowNibName:@"SUUpdateAlert"];
if (self)
{
- hostBundle = [hb retain];
+ host = [hb retain];
updateItem = [item retain];
[self setShouldCascadeWindows:NO];
}
@@ -28,7 +28,7 @@ - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb
- (void)dealloc
{
[updateItem release];
- [hostBundle release];
+ [host release];
[super dealloc];
}
@@ -85,7 +85,7 @@ - (void)displayReleaseNotes
- (BOOL)showsReleaseNotes
{
- NSNumber *shouldShowReleaseNotes = [hostBundle objectForInfoDictionaryKey:SUShowReleaseNotesKey];
+ NSNumber *shouldShowReleaseNotes = [host objectForInfoDictionaryKey:SUShowReleaseNotesKey];
if (shouldShowReleaseNotes == nil)
return YES; // defaults to YES
else
@@ -94,11 +94,11 @@ - (BOOL)showsReleaseNotes
- (BOOL)allowsAutomaticUpdates
{
- if (![[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue])
+ if (![[host objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue])
return NO; // Automatic updating requires DSA-signed updates
- if (![hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey])
+ if (![host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey])
return YES; // defaults to YES
- return [[hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue];
+ return [[host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue];
}
- (void)awakeFromNib
@@ -140,17 +140,17 @@ - (BOOL)windowShouldClose:note
- (NSImage *)applicationIcon
{
- return [hostBundle icon];
+ return [host icon];
}
- (NSString *)titleText
{
- return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [hostBundle name]];
+ return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [host name]];
}
- (NSString *)descriptionText
{
- return [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available\u2014you have %@. Would you like to download it now?", nil), [hostBundle name], [updateItem displayVersionString], [hostBundle displayVersion]];
+ return [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available\u2014you have %@. Would you like to download it now?", nil), [host name], [updateItem displayVersionString], [host displayVersion]];
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:frame
5 SUUpdateDriver.h
View
@@ -13,12 +13,15 @@
extern NSString *SUUpdateDriverFinishedNotification;
+@class SUHost;
@interface SUUpdateDriver : NSObject
{
+ SUHost *host;
+
BOOL finished;
id delegate;
}
-- (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb;
+- (void)checkForUpdatesAtURL:(NSURL *)appcastURL host:(SUHost *)hb;
- (void)abortUpdate;
- (BOOL)finished;
4 SUUpdateDriver.m
View
@@ -11,9 +11,9 @@
NSString *SUUpdateDriverFinishedNotification = @"SUUpdateDriverFinished";
@implementation SUUpdateDriver
-- (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
+- (void)checkForUpdatesAtURL:(NSURL *)appcastURL host:(SUHost *)h
{
- [NSException raise:@"SUAbstractDriverError" format:@"Don't use SUUpdateDriver directly; use a subclass."];
+ host = [h retain];
}
- (void)abortUpdate
5 SUUpdatePermissionPrompt.h
View
@@ -16,15 +16,16 @@ typedef enum {
SUDoNotAutomaticallyCheck
} SUPermissionPromptResult;
+@class SUHost;
@interface SUUpdatePermissionPrompt : SUWindowController {
- NSBundle *hostBundle;
+ SUHost *host;
id delegate;
IBOutlet NSTextField *descriptionTextField;
IBOutlet NSView *moreInfoView;
IBOutlet NSButton *moreInfoButton;
BOOL isShowingMoreInfo, shouldSendProfile;
}
-+ (void)promptWithHostBundle:(NSBundle *)hb delegate:(id)d;
++ (void)promptWithHost:(SUHost *)hb delegate:(id)d;
- (IBAction)toggleMoreInfo:(id)sender;
- (IBAction)finishPrompt:(id)sender;
@end
22 SUUpdatePermissionPrompt.m
View
@@ -13,15 +13,15 @@ @implementation SUUpdatePermissionPrompt
- (BOOL)shouldAskAboutProfile
{
- return [[hostBundle objectForInfoDictionaryKey:SUEnableSystemProfilingKey] boolValue];
+ return [[host objectForInfoDictionaryKey:SUEnableSystemProfilingKey] boolValue];
}
-- (id)initWithHostBundle:(NSBundle *)hb delegate:(id)d
+- (id)initWithHost:(SUHost *)hb delegate:(id)d
{
- self = [super initWithHostBundle:hb windowNibName:@"SUUpdatePermissionPrompt"];
+ self = [super initWithHost:hb windowNibName:@"SUUpdatePermissionPrompt"];
if (self)
{
- hostBundle = [hb retain];
+ host = [hb retain];
delegate = [d retain];
isShowingMoreInfo = NO;
shouldSendProfile = [self shouldAskAboutProfile];
@@ -30,9 +30,9 @@ - (id)initWithHostBundle:(NSBundle *)hb delegate:(id)d
return self;
}
-+ (void)promptWithHostBundle:(NSBundle *)hb delegate:(id)d
++ (void)promptWithHost:(SUHost *)hb delegate:(id)d
{
- id prompt = [[[self class] alloc] initWithHostBundle:hb delegate:d];
+ id prompt = [[[self class] alloc] initWithHost:hb delegate:d];
[NSApp runModalForWindow:[prompt window]];
}
@@ -48,23 +48,23 @@ - (void)awakeFromNib
- (void)dealloc
{
- [hostBundle release];
+ [host release];
[super dealloc];
}
- (NSImage *)icon
{
- return [hostBundle icon];
+ return [host icon];
}
- (NSString *)promptDescription
{
- return [NSString stringWithFormat:SULocalizedString(@"Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu.", nil), [hostBundle name]];
+ return [NSString stringWithFormat:SULocalizedString(@"Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu.", nil), [host name]];
}
- (NSArray *)systemProfileInformationArray
{
- return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHostBundle:hostBundle];
+ return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHost:host];
}
- (IBAction)toggleMoreInfo:(id)sender
@@ -116,7 +116,7 @@ - (IBAction)finishPrompt:(id)sender
{
if (![delegate respondsToSelector:@selector(updatePermissionPromptFinishedWithResult:)])
[NSException raise:@"SUInvalidDelegate" format:@"SUUpdatePermissionPrompt's delegate (%@) doesn't respond to updatePermissionPromptFinishedWithResult:!", delegate];
- [[SUUserDefaults standardUserDefaults] setBool:shouldSendProfile forKey:SUSendProfileInfoKey];
+ [host setBool:shouldSendProfile forUserDefaultsKey:SUSendProfileInfoKey];
[delegate updatePermissionPromptFinishedWithResult:([sender tag] == 1 ? SUAutomaticallyCheck : SUDoNotAutomaticallyCheck)];
[[self window] close];
[NSApp stopModal];
27 SUUpdater.h
View
@@ -17,11 +17,12 @@
NSTimer *checkTimer;
SUUpdateDriver *driver;
- NSBundle *hostBundle;
+ SUHost *host;
IBOutlet id delegate;
}
+ (SUUpdater *)sharedUpdater;
++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle;
- (void)setDelegate:(id)delegate;
@@ -37,10 +38,6 @@
// This forces an update to begin with a particular driver (see SU*UpdateDriver.h)
- (void)checkForUpdatesWithDriver:(SUUpdateDriver *)driver;
-// For non-.app updates:
-// Call this when your bundle is loaded to tell Sparkle what to update.
-- (void)setHostBundle:(NSBundle *)hostBundle;
-
// Call this to appropriately reschedule or cancel the update checking timer if preferences for time interval or automatic checks change.
// If you're using a .app, this'll be picked up automatically via NSUserDefaultsController, but for non-.apps, there's no way to observe changes.
- (void)updatePreferencesChanged;
@@ -51,39 +48,39 @@
@interface NSObject (SUUpdaterDelegateInformalProtocol)
// This method allows you to add extra parameters to the appcast URL, potentially based on whether or not
// Sparkle will also be sending along the system profile. This method should return an array of dictionaries with the following keys:
-- (NSArray *)feedParametersForHostBundle:(NSBundle *)bundle sendingSystemProfile:(BOOL)sendingProfile;
+- (NSArray *)feedParametersForHost:(SUHost *)bundle sendingSystemProfile:(BOOL)sendingProfile;
// Use this to override the default behavior for Sparkle prompting the user about automatic update checks.
-- (BOOL)shouldPromptForPermissionToCheckForUpdatesToHostBundle:(NSBundle *)bundle;
+- (BOOL)shouldPromptForPermissionToCheckForUpdatesToHost:(SUHost *)bundle;
// Implement this if you want to do some special handling with the appcast once it finishes loading.
-- (void)appcastDidFinishLoading:(SUAppcast *)appcast forHostBundle:(NSBundle *)bundle;
+- (void)appcastDidFinishLoading:(SUAppcast *)appcast forHost:(SUHost *)bundle;
// If you're using special logic or extensions in your appcast, implement this to use your own logic for finding
// a valid update, if any, in the given appcast.
-- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forHostBundle:(NSBundle *)bundle;
+- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forHost:(SUHost *)bundle;
// Sent when a valid update is found by the update driver.
-- (void)didFindValidUpdate:(SUAppcastItem *)update toHostBundle:(NSBundle *)bundle;
+- (void)didFindValidUpdate:(SUAppcastItem *)update toHost:(SUHost *)bundle;
// Sent when a valid update is not found.
-- (void)didNotFindUpdateToHostBundle:(NSBundle *)hb;
+- (void)didNotFindUpdateToHost:(SUHost *)hb;
// Sent when the user makes a choice in the update alert dialog (install now / remind me later / skip this version).
-- (void)userChoseAction:(SUUpdateAlertChoice)action forUpdate:(SUAppcastItem *)update toHostBundle:(NSBundle *)bundle;
+- (void)userChoseAction:(SUUpdateAlertChoice)action forUpdate:(SUAppcastItem *)update toHost:(SUHost *)bundle;
// Sent immediately before installing the specified update.
-- (void)updateWillInstall:(SUAppcastItem *)update toHostBundle:(NSBundle *)bundle;
+- (void)updateWillInstall:(SUAppcastItem *)update toHost:(SUHost *)bundle;
// Return YES to delay the relaunch until you do some processing; invoke the given NSInvocation to continue.
-- (BOOL)shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update toHostBundle:(NSBundle *)hostBundle untilInvoking:(NSInvocation *)invocation;
+- (BOOL)shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update toHost:(SUHost *)hostBundle untilInvoking:(NSInvocation *)invocation;
// Called immediately before relaunching.
- (void)updaterWillRelaunchApplication;
// This method allows you to provide a custom version comparator.
// If you don't implement this method or return nil, the standard version comparator will be used.
-- (id <SUVersionComparison>)versionComparatorForHostBundle:(NSBundle *)hb;
+- (id <SUVersionComparison>)versionComparatorForHost:(SUHost *)hb;
@end
240 SUUpdater.m
View
@@ -15,6 +15,10 @@ - (BOOL)automaticallyUpdates;
- (BOOL)shouldScheduleUpdateCheck;
- (void)scheduleNextUpdateCheck;
- (NSTimeInterval)checkInterval;
+- (void)registerAsObserver;
+- (void)unregisterAsObserver;
+- (void)updateDriverDidFinish:(NSNotification *)note;
+- initForBundle:(NSBundle *)bundle;
- (NSURL *)feedURL;
@end
@@ -22,71 +26,107 @@ @implementation SUUpdater
#pragma mark Initialization
-static SUUpdater *sharedUpdater = nil;
+static NSMutableDictionary *sharedUpdaters = nil;
+static NSString *SUUpdaterDefaultsObservationContext = @"SUUpdaterDefaultsObservationContext";
-// SUUpdater's a singleton now! And I'm enforcing it!
-// This will probably break the world if you try to write a Sparkle-enabled plugin for a Sparkle-enabled app.
+ (SUUpdater *)sharedUpdater
{
- if (sharedUpdater == nil)
- sharedUpdater = [[[self class] alloc] init];
- return sharedUpdater;
+ return [self updaterForBundle:[NSBundle mainBundle]];
}
-- (id)init
+// SUUpdater has a singleton for each bundle. We use the fact that NSBundle instances are also singletons, so we can use them as keys. If you don't trust that you can also use the identifier as key
++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle
+{
+ if (bundle == nil) bundle = [NSBundle mainBundle];
+ id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]];
+ if (updater == nil)
+ updater = [[[self class] alloc] initWithBundle:bundle];
+ return updater;
+}
+
+// This is the designated initializer for SUUpdater, important for subclasses
+- initForBundle:(NSBundle *)bundle
{
self = [super init];
- if (sharedUpdater)
+ if (bundle == nil) bundle = [NSBundle mainBundle];
+ id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]];
+ if (updater)
{
[self release];
- self = sharedUpdater;
+ self = [updater retain];
}
- else if (self != nil)
+ else if (self)
{
- sharedUpdater = self;
- [self setHostBundle:[NSBundle mainBundle]];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:NSApp];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(driverDidFinish:) name:SUUpdateDriverFinishedNotification object:nil];
- [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey] options:0 context:NULL];
- [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey] options:0 context:NULL];
+ if (sharedUpdaters == nil)
+ sharedUpdaters = [[NSMutableDictionary alloc] init];
+ [sharedUpdaters setObject:self forKey:[NSValue valueWithNonretainedObject:bundle]];
+ host = [[SUHost alloc] initWithBundle:bundle];
+ [self registerAsObserver];
}
return self;
}
+// This will be used when the updater is instantiated in a nib such as MainMenu
+- (id)init
+{
+ return [self initForBundle:[NSBundle mainBundle]];
+}
+
- (void)applicationDidFinishLaunching:(NSNotification *)note
{
- // If the user has been asked about automatic checks and said no, get out of here.
- if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] &&
- [[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKey] == NO) { return; }
-
- // Does the delegate want to take care of the logic for when we should ask permission to update?
- if ([delegate respondsToSelector:@selector(shouldPromptForPermissionToCheckForUpdatesToHostBundle:)])
- {
- if ([delegate shouldPromptForPermissionToCheckForUpdatesToHostBundle:hostBundle])
- [SUUpdatePermissionPrompt promptWithHostBundle:hostBundle delegate:self];
- }
- // Has he been asked already? And don't ask if the host has a default value set in its Info.plist.
- else if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] == nil &&
- [hostBundle objectForInfoDictionaryKey:SUEnableAutomaticChecksKey] == nil)
- {
- if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKeyOld])
- [[SUUserDefaults standardUserDefaults] setBool:[[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKeyOld] forKey:SUEnableAutomaticChecksKey];
- // Now, we don't want to ask the user for permission to do a weird thing on the first launch.
- // We wait until the second launch.
- else if ([[SUUserDefaults standardUserDefaults] boolForKey:SUHasLaunchedBeforeKey] == NO)
- [[SUUserDefaults standardUserDefaults] setBool:YES forKey:SUHasLaunchedBeforeKey];
- else
- [SUUpdatePermissionPrompt promptWithHostBundle:hostBundle delegate:self];
+ BOOL shouldPrompt = NO;
+
+ // If the user has been asked about automatic checks, don't bother prompting
+ if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKey])
+ {
+ shouldPrompt = NO;
+ }
+ // Does the delegate want to take care of the logic for when we should ask permission to update?
+ else if ([delegate respondsToSelector:@selector(shouldPromptForPermissionToCheckForUpdatesToHostBundle:)])
+ {
+ shouldPrompt = [delegate shouldPromptForPermissionToCheckForUpdatesToHost:host];
+ }
+ // Has he been asked already? And don't ask if the host has a default value set in its Info.plist.
+ else if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKey] == nil &&
+ [host objectForInfoDictionaryKey:SUEnableAutomaticChecksKey] == nil)
+ {
+ if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKeyOld])
+ [host setBool:[host boolForUserDefaultsKey:SUEnableAutomaticChecksKeyOld] forUserDefaultsKey:SUEnableAutomaticChecksKey];
+ // Now, we don't want to ask the user for permission to do a weird thing on the first launch.
+ // We wait until the second launch.
+ else if ([host boolForUserDefaultsKey:SUHasLaunchedBeforeKey] == NO)
+ [host setBool:YES forUserDefaultsKey:SUHasLaunchedBeforeKey];
+ else
+ shouldPrompt = YES;
+ }
+
+ if (shouldPrompt)
+ {
+ [SUUpdatePermissionPrompt promptWithHost:host delegate:self];
+ // We start the update checks and register as observer for changes after the prompt finishes
}
-
- // We check if the user's said they want updates, or they haven't said anything, and the default is set to checking.
- [self scheduleNextUpdateCheck];
+ else
+ {
+ // We check if the user's said they want updates, or they haven't said anything, and the default is set to checking.
+ [self scheduleNextUpdateCheck];
+ }
}
- (void)updatePermissionPromptFinishedWithResult:(SUPermissionPromptResult)result
{
- [[SUUserDefaults standardUserDefaults] setBool:(result == SUAutomaticallyCheck) forKey:SUEnableAutomaticChecksKey];
- [self scheduleNextUpdateCheck];
+ BOOL automaticallyCheck = (result == SUAutomaticallyCheck);
+ [host setBool:automaticallyCheck forUserDefaultsKey:SUEnableAutomaticChecksKey];
+ // Schedule checks, but make sure we ignore the delayed call from KVO
+ [self updatePreferencesChanged];
+}
+
+- (void)updateDriverDidFinish:(NSNotification *)note
+{
+ if ([note object] == driver && [driver finished])
+ {
+ [driver release]; driver = nil;
+ [self scheduleNextUpdateCheck];
+ }
}
- (void)scheduleNextUpdateCheck
@@ -99,7 +139,7 @@ - (void)scheduleNextUpdateCheck
if (![self shouldScheduleUpdateCheck]) return;
// How long has it been since last we checked for an update?
- NSDate *lastCheckDate = [[SUUserDefaults standardUserDefaults] objectForKey:SULastCheckTimeKey];
+ NSDate *lastCheckDate = [host objectForUserDefaultsKey:SULastCheckTimeKey];
if (!lastCheckDate) { lastCheckDate = [NSDate distantPast]; }
NSTimeInterval intervalSinceCheck = [[NSDate date] timeIntervalSinceDate:lastCheckDate];
@@ -127,37 +167,70 @@ - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)d
if ([self updateInProgress]) { return; }
if (checkTimer) { [checkTimer invalidate]; checkTimer = nil; }
+ // A value in the user defaults overrides one in the Info.plist (so preferences panels can be created wherein users choose between beta / release feeds).
+ NSString *appcastString = [host objectForUserDefaultsKey:SUFeedURLKey];
+ if (!appcastString)
+ appcastString = [host objectForInfoDictionaryKey:SUFeedURLKey];
+ if (!appcastString)
+ [NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the SUFeedURLKey in either the Info.plist or the user defaults!"];
+
driver = [d retain];
if ([driver delegate] == nil) { [driver setDelegate:delegate]; }
- [driver checkForUpdatesAtURL:[self feedURL] hostBundle:hostBundle];
+ [driver checkForUpdatesAtURL:[self feedURL] host:host];
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+- (void)registerAsObserver
{
- if (object == [NSUserDefaultsController sharedUserDefaultsController] && ([keyPath hasSuffix:SUScheduledCheckIntervalKey] || [keyPath hasSuffix:SUEnableAutomaticChecksKey]))
- {
- [self updatePreferencesChanged];
- }
- else
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:NSApp];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDriverDidFinish:) name:SUUpdateDriverFinishedNotification object:nil];
+ // No sense observing the shared NSUserDefaultsController when we're not updating the main bundle.
+ if ([host bundle] != [NSBundle mainBundle]) return;
+ [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey] options:0 context:SUUpdaterDefaultsObservationContext];
+ [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey] options:0 context:SUUpdaterDefaultsObservationContext];
+}
+
+- (void)unregisterAsObserver
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ // Removing self as a KVO observer if no observer was registered leads to an NSException. But we don't care.
+ @try
{
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey]];
+ [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey]];
}
+ @catch (NSException *e) { }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if (context == SUUpdaterDefaultsObservationContext)
+ {
+ // Allow a small delay, because perhaps the user or developer wants to change both preferences. This allows the developer to interpret a zero check interval as a sign to disable automatic checking.
+ // Or we may get this from the developer and from our own KVO observation, this will effectively coalesce them.
+ [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(updatePreferencesChanged) object:nil];
+ [self performSelector:@selector(updatePreferencesChanged) withObject:nil afterDelay:15];
+ [self updatePreferencesChanged];
+ }
+ else
+ {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
}
- (void)updatePreferencesChanged
{
- [self scheduleNextUpdateCheck];
+ [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(updatePreferencesChanged) object:nil];
+ [self scheduleNextUpdateCheck];
}
- (BOOL)shouldScheduleUpdateCheck
{
// Breaking this down for readability:
// If the user says he wants automatic update checks, let's do it.
- if ([[SUUserDefaults standardUserDefaults] boolForKey:SUEnableAutomaticChecksKey] == YES)
+ if ([host boolForUserDefaultsKey:SUEnableAutomaticChecksKey] == YES)
return YES;
// If the user hasn't said anything, but the developer says we should do it, let's do it.
- if ([[SUUserDefaults standardUserDefaults] objectForKey:SUEnableAutomaticChecksKey] == nil &&
- [[hostBundle objectForInfoDictionaryKey:SUEnableAutomaticChecksKey] boolValue] == YES)
+ if ([host objectForUserDefaultsKey:SUEnableAutomaticChecksKey] == nil && [host boolForInfoDictionaryKey:SUEnableAutomaticChecksKey] == YES)
return YES;
return NO; // Otherwise, don't bother.
}
@@ -165,16 +238,15 @@ - (BOOL)shouldScheduleUpdateCheck
- (BOOL)automaticallyUpdates
{
// If the SUAllowsAutomaticUpdatesKey exists and is set to NO, return NO.
- if ([hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] &&
- [[hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue] == NO)
+ if ([host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] && [host boolForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] == NO)
return NO;
// If we're not using DSA signatures, we aren't going to trust any updates automatically.
- if ([[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue] != YES)
+ if ([host boolForInfoDictionaryKey:SUExpectsDSASignatureKey] != YES)
return NO;
// If there's no setting, or it's set to no, we're not automatically updating.
- if ([[SUUserDefaults standardUserDefaults] boolForKey:SUAutomaticallyUpdateKey] != YES)
+ if ([host boolForUserDefaultsKey:SUAutomaticallyUpdateKey] != YES)
return NO;
return YES; // Otherwise, we're good to go.
@@ -183,9 +255,9 @@ - (BOOL)automaticallyUpdates
- (NSURL *)_baseFeedURL
{
// A value in the user defaults overrides one in the Info.plist (so preferences panels can be created wherein users choose between beta / release feeds).
- NSString *appcastString = [[SUUserDefaults standardUserDefaults] objectForKey:SUFeedURLKey];
+ NSString *appcastString = [host objectForUserDefaultsKey:SUFeedURLKey];
if (!appcastString)
- appcastString = [hostBundle objectForInfoDictionaryKey:SUFeedURLKey];
+ appcastString = [host objectForInfoDictionaryKey:SUFeedURLKey];
if (!appcastString) // Can't find an appcast string!
[NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the SUFeedURLKey in either the Info.plist or the user defaults!"];
NSCharacterSet* quoteSet = [NSCharacterSet characterSetWithCharactersInString: @"\"\'"]; // Some feed publishers add quotes; strip 'em.
@@ -197,12 +269,12 @@ - (NSURL *)feedURL
NSURL *baseFeedURL = [self _baseFeedURL];
// Determine all the parameters we're attaching to the base feed URL.
- BOOL sendingSystemProfile = ([[SUUserDefaults standardUserDefaults] boolForKey:SUSendProfileInfoKey] == YES);
+ BOOL sendingSystemProfile = ([host boolForUserDefaultsKey:SUSendProfileInfoKey] == YES);
NSArray *parameters = [NSArray array];
if ([delegate respondsToSelector:@selector(feedParametersForHostBundle:sendingSystemProfile:)])
- parameters = [parameters arrayByAddingObjectsFromArray:[delegate feedParametersForHostBundle:hostBundle sendingSystemProfile:sendingSystemProfile]];
+ parameters = [parameters arrayByAddingObjectsFromArray:[delegate feedParametersForHost:host sendingSystemProfile:sendingSystemProfile]];
if (sendingSystemProfile)
- parameters = [parameters arrayByAddingObjectsFromArray:[hostBundle systemProfile]];
+ parameters = [parameters arrayByAddingObjectsFromArray:[host systemProfile]];
if (parameters == nil || [parameters count] == 0) { return baseFeedURL; }
// Build up the parameterized URL.
@@ -222,10 +294,10 @@ - (NSTimeInterval)checkInterval
{
NSTimeInterval checkInterval = 0;
// Find the stored check interval. User defaults override Info.plist.
- if ([[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey])
- checkInterval = [[[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey] doubleValue];
- else if ([hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey])
- checkInterval = [[hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey] doubleValue];
+ if ([host objectForUserDefaultsKey:SUScheduledCheckIntervalKey])
+ checkInterval = [[host objectForUserDefaultsKey:SUScheduledCheckIntervalKey] doubleValue];
+ else if ([host objectForInfoDictionaryKey:SUScheduledCheckIntervalKey])
+ checkInterval = [[host objectForInfoDictionaryKey:SUScheduledCheckIntervalKey] doubleValue];
if (checkInterval < SU_MIN_CHECK_INTERVAL) // This can also mean one that isn't set.
checkInterval = SU_DEFAULT_CHECK_INTERVAL;
@@ -234,9 +306,9 @@ - (NSTimeInterval)checkInterval
- (void)dealloc
{
- [hostBundle release];
+ [self unregisterAsObserver];
+ [host release];
if (checkTimer) { [checkTimer invalidate]; }
- [[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
@@ -252,25 +324,25 @@ - (void)setDelegate:aDelegate
delegate = aDelegate;
}
-- (void)setHostBundle:(NSBundle *)hb
-{
- if (hostBundle == hb) return;
- [hostBundle release];
- hostBundle = [hb retain];
- [[SUUserDefaults standardUserDefaults] setIdentifier:[hostBundle bundleIdentifier]];
-}
-
- (BOOL)updateInProgress
{
return driver && ([driver finished] == NO);
}
-- (void)driverDidFinish:(NSNotification *)notification
+// Redirect driver delegate methods to our delegate
+/*
+- (void)appcastDidFinishLoading:(SUAppcast *)appcast forHost:(SUHost *)bundle
{
- if ([notification object] != driver) return;
- [driver release];
- driver = nil;
- [self scheduleNextUpdateCheck];
+ if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:forHost:)])
+ [delegate appcastDidFinishLoading:ac forHost:bundle];
}
+- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forHost:(SUHost *)bundle
+{
+ if ([delegate respondsToSelector:@selector(bestValidUpdateInAppcast:forHost:)]) // Does the delegate want to handle it?
+ return [delegate bestValidUpdateInAppcast:ac forHost:bundle];
+ else
+ return nil;
+}*/
+
@end
41 SUUserDefaults.h
View
@@ -1,41 +0,0 @@
-//
-// SUUserDefaults.h
-// Sparkle
-//
-// Created by Andy Matuschak on 12/21/07.
-// Copyright 2007 Andy Matuschak. All rights reserved.
-//
-
-#ifndef SUUSERDEFAULTS_H
-#define SUUSERDEFAULTS_H
-
-/*!
- @class
- @abstract A substitute for NSUserDefaults that will work with arbitrary bundle identifiers.
- @discussion Make sure you call -setIdentifier: before using SUUserDefaults. The other methods in this class work just like those in NSUserDefaults.
-*/
-
-@interface SUUserDefaults : NSObject {
- NSString *identifier;
-}
-
-/*!
- @method
- @abstract Returns a singleton instance of the user defaults class.
-*/
-+ (SUUserDefaults *)standardUserDefaults;
-
-/*!
- @method
- @abstract Sets which bundle identifier to use when setting and retrieving defaults.
- @discussion It is imperative that you set the identifier through this method before trying to set or retrieve defaults.
-*/
-- (void)setIdentifier:(NSString *)identifier;
-
-- (id)objectForKey:(NSString *)defaultName;
-- (void)setObject:(id)value forKey:(NSString *)defaultName;
-- (BOOL)boolForKey:(NSString *)defaultName;
-- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
-@end
-
-#endif
97 SUUserDefaults.m
View
@@ -1,97 +0,0 @@
-//
-// SUUserDefaults.m
-// Sparkle
-//
-// Created by Andy Matuschak on 12/21/07.
-// Copyright 2007 Andy Matuschak. All rights reserved.
-//
-
-#import "Sparkle.h"
-#import "SUUserDefaults.h"
-
-@implementation SUUserDefaults
-
-+ (SUUserDefaults *)standardUserDefaults
-{
- static SUUserDefaults *standardUserDefaults = nil;
- if (standardUserDefaults == nil)
- standardUserDefaults = [[SUUserDefaults alloc] init];
- return standardUserDefaults;
-}
-
-- (void)dealloc
-{
- [identifier release];
- [super dealloc];
-}
-
-- (void)setIdentifier:(NSString *)anIdentifier
-{
- if (identifier != anIdentifier)
- {
- [identifier release];
- identifier = [anIdentifier copy];
- }
-}
-
-- (void)verifyIdentifier
-{
- if (identifier == nil)
- [NSException raise:@"SUUserDefaultsMissingIdentifier" format:@"You must set the SUUserDefaults identifier before using it."];
-}
-
-- (id)objectForKey:(NSString *)defaultName
-{
- [self verifyIdentifier];
- CFPropertyListRef obj = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)identifier);
- // Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistrationDomain, so anything
- // passed into -[NSUserDefaults registerDefaults:] is ignored. The following line falls
- // back to using NSUserDefaults, but only if the host bundle is the main bundle, and no value
- // is found elsewhere.
- if (obj == NULL && [identifier isEqualToString:[[NSBundle mainBundle] bundleIdentifier]])
- obj = [[NSUserDefaults standardUserDefaults] objectForKey:defaultName];
- id result = nil;
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
- result = [NSMakeCollectable(obj) autorelease];
-#endif
- return result ?: (id)obj;
-}
-
-- (void)setObject:(id)value forKey:(NSString *)defaultName;
-{
- [self verifyIdentifier];
- CFPreferencesSetValue((CFStringRef)defaultName, value, (CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- CFPreferencesSynchronize((CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- // If anything's bound to this through an NSUserDefaultsController, it won't know that anything's changed.
- // We can't get an NSUserDefaults object for anything other than the standard one for the app, so this won't work for bundles.
- // But it's the best we can do: this will make NSUserDefaultsControllers know about the changes that have been made.
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-- (BOOL)boolForKey:(NSString *)defaultName
-{
- BOOL value;
- [self verifyIdentifier];
- CFPropertyListRef plr = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)identifier);
- if (plr == NULL)
- value = NO;
- else {
- value = (BOOL)CFBooleanGetValue((CFBooleanRef)plr);
- CFRelease(plr);
- }
-
- return value;
-}
-
-- (void)setBool:(BOOL)value forKey:(NSString *)defaultName
-{
- [self verifyIdentifier];
- CFPreferencesSetValue((CFStringRef)defaultName, (CFBooleanRef)[NSNumber numberWithBool:value], (CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- CFPreferencesSynchronize((CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- // If anything's bound to this through an NSUserDefaultsController, it won't know that anything's changed.
- // We can't get an NSUserDefaults object for anything other than the standard one for the app, so this won't work for bundles.
- // But it's the best we can do: this will make NSUserDefaultsControllers know about the changes that have been made.
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-@end
6 SUUserInitiatedUpdateDriver.m
View
@@ -11,10 +11,10 @@
@implementation SUUserInitiatedUpdateDriver
-- (void)checkForUpdatesAtURL:(NSURL *)appcastURL hostBundle:(NSBundle *)hb
+- (void)checkForUpdatesAtURL:(NSURL *)appcastURL host:(SUHost *)hb
{
- [super checkForUpdatesAtURL:appcastURL hostBundle:hb];
- checkingController = [[SUStatusController alloc] initWithHostBundle:hb];
+ [super checkForUpdatesAtURL:appcastURL host:hb];
+ checkingController = [[SUStatusController alloc] initWithHost:hb];
[[checkingController window] center]; // Force the checking controller to load its window.
[checkingController beginActionWithTitle:SULocalizedString(@"Checking for updates\u2026", nil) maxProgressValue:0 statusText:nil];
[checkingController setButtonTitle:SULocalizedString(@"Cancel", nil) target:self action:@selector(cancelCheckForUpdates:) isDefault:NO];
3  SUWindowController.h
View
@@ -11,9 +11,10 @@
#import <Cocoa/Cocoa.h>
+@class SUHost;
@interface SUWindowController : NSWindowController { }
// We use this instead of plain old NSWindowController initWithWindowNibName so that we'll be able to find the right path when running in a bundle loaded from another app.
-- (id)initWithHostBundle:(NSBundle *)hb windowNibName:(NSString *)nibName;
+- (id)initWithHost:(SUHost *)hb windowNibName:(NSString *)nibName;
@end
#endif
2  SUWindowController.m
View
@@ -10,7 +10,7 @@
@implementation SUWindowController
-- (id)initWithHostBundle:(NSBundle *)hb windowNibName:(NSString *)nibName
+- (id)initWithHost:(SUHost *)hb windowNibName:(NSString *)nibName
{
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:nibName ofType:@"nib"];
if (path == nil) // Slight hack to resolve issues with running Sparkle in debug configurations.
3  Sparkle.h
View
@@ -25,7 +25,6 @@
// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless
// there are name-space collisions) so we can list all of them to start with:
-#import "NSBundle+SUAdditions.h"
#import "NSFileManager+Aliases.h"
#import "NSFileManager+Authentication.h"
#import "NSFileManager+Verification.h"
@@ -40,6 +39,7 @@
#import "SUAutomaticUpdateDriver.h"
#import "SUBasicUpdateDriver.h"
#import "SUConstants.h"
+#import "SUHost.h"
#import "SUInstaller.h"
#import "SUProbingUpdateDriver.h"
#import "SUScheduledUpdateDriver.h"
@@ -53,7 +53,6 @@
#import "SUUpdater.h"
#import "SUUpdatePermissionPrompt.h"
#import "SUUserInitiatedUpdateDriver.h"
-#import "SUUserDefaults.h"
#import "SUVersionComparisonProtocol.h"
#import "SUWindowController.h"
26 Sparkle.xcodeproj/project.pbxproj
View
@@ -55,13 +55,9 @@
618FA5230DAE8E8A0026945C /* SUPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */; };
6196CFF909C72148000DC222 /* SUStatusController.h in Headers */ = {isa = PBXBuildFile; fileRef = 6196CFE309C71ADE000DC222 /* SUStatusController.h */; settings = {ATTRIBUTES = (Public, ); }; };
6196CFFA09C72149000DC222 /* SUStatusController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6196CFE409C71ADE000DC222 /* SUStatusController.m */; };
- 61A225930D1C3ADD00430CCD /* NSBundle+SUAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A225910D1C3ADD00430CCD /* NSBundle+SUAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61A225940D1C3ADD00430CCD /* NSBundle+SUAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225920D1C3ADD00430CCD /* NSBundle+SUAdditions.m */; };
61A2259E0D1C495D00430CCD /* SUVersionComparisonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
61A225A40D1C4AC000430CCD /* SUStandardVersionComparator.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */; settings = {ATTRIBUTES = (Public, ); }; };
61A225A50D1C4AC000430CCD /* SUStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */; };
- 61A2262B0D1C58FD00430CCD /* SUUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A226290D1C58FD00430CCD /* SUUserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61A2262C0D1C58FD00430CCD /* SUUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A2262A0D1C58FD00430CCD /* SUUserDefaults.m */; };
61A2279C0D1CEE7600430CCD /* SUSystemProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A2279A0D1CEE7600430CCD /* SUSystemProfiler.h */; settings = {ATTRIBUTES = (Public, ); }; };
61A2279D0D1CEE7600430CCD /* SUSystemProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */; };
61A354550DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A354530DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -95,6 +91,8 @@
61C1F2790DA066C2007ACE13 /* NSFileManager+ExtendedAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 61C1F2770DA066C2007ACE13 /* NSFileManager+ExtendedAttributes.m */; };
61C46F340D9C54F300B06326 /* SUUpdatePermissionPrompt.nib in Resources */ = {isa = PBXBuildFile; fileRef = 61C46F330D9C54F300B06326 /* SUUpdatePermissionPrompt.nib */; };
61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6129C0B90E0B79810062CE76 /* SUPipedUnarchiver.m */; };
+ 61EF67560E25B58D00F754E0 /* SUHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 61EF67550E25B58D00F754E0 /* SUHost.m */; };
+ 61EF67590E25C5B400F754E0 /* SUHost.h in Headers */ = {isa = PBXBuildFile; fileRef = 61EF67580E25C5B400F754E0 /* SUHost.h */; };
61F83F720DBFE140006FDD30 /* SUBasicUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61F83F700DBFE137006FDD30 /* SUBasicUpdateDriver.m */; };
61F83F740DBFE141006FDD30 /* SUBasicUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61F83F6F0DBFE137006FDD30 /* SUBasicUpdateDriver.h */; settings = {ATTRIBUTES = (Public, ); }; };
DAAEFC9B0DA5722F0051E0D0 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
@@ -206,13 +204,9 @@
619B17210E1E9D0800E72754 /* de */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = de; path = de.lproj/SUAutomaticUpdateAlert.nib; sourceTree = "<group>"; };
619B17220E1E9D0800E72754 /* de */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = de; path = de.lproj/SUUpdateAlert.nib; sourceTree = "<group>"; };
619B17230E1E9D0800E72754 /* de */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = de; path = de.lproj/SUUpdatePermissionPrompt.nib; sourceTree = "<group>"; };
- 61A225910D1C3ADD00430CCD /* NSBundle+SUAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+SUAdditions.h"; sourceTree = "<group>"; };
- 61A225920D1C3ADD00430CCD /* NSBundle+SUAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+SUAdditions.m"; sourceTree = "<group>"; };
61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUVersionComparisonProtocol.h; sourceTree = "<group>"; };
61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUStandardVersionComparator.h; sourceTree = "<group>"; };
61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUStandardVersionComparator.m; sourceTree = "<group>"; };
- 61A226290D1C58FD00430CCD /* SUUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUserDefaults.h; sourceTree = "<group>"; };
- 61A2262A0D1C58FD00430CCD /* SUUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUserDefaults.m; sourceTree = "<group>"; };
61A2279A0D1CEE7600430CCD /* SUSystemProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUSystemProfiler.h; sourceTree = "<group>"; };
61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUSystemProfiler.m; sourceTree = "<group>"; };
61A354530DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUserInitiatedUpdateDriver.h; sourceTree = "<group>"; };
@@ -249,6 +243,8 @@
61C1F2760DA066C2007ACE13 /* NSFileManager+ExtendedAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+ExtendedAttributes.h"; sourceTree = "<group>"; };
61C1F2770DA066C2007ACE13 /* NSFileManager+ExtendedAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+ExtendedAttributes.m"; sourceTree = "<group>"; };
61C46F350D9C54F300B06326 /* en */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = en; path = en.lproj/SUUpdatePermissionPrompt.nib; sourceTree = "<group>"; };
+ 61EF67550E25B58D00F754E0 /* SUHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUHost.m; sourceTree = "<group>"; };
+ 61EF67580E25C5B400F754E0 /* SUHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUHost.h; sourceTree = "<group>"; };
61F3651A0E18987B007ECA02 /* es */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = es; path = es.lproj/SUUpdatePermissionPrompt.nib; sourceTree = "<group>"; };
61F3652A0E189883007ECA02 /* es */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = es; path = es.lproj/SUUpdateAlert.nib; sourceTree = "<group>"; };
61F3652B0E189883007ECA02 /* es */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = es; path = es.lproj/SUAutomaticUpdateAlert.nib; sourceTree = "<group>"; };
@@ -473,8 +469,8 @@
61B5F8F309C4CE5900B25A18 /* Other Sources */ = {
isa = PBXGroup;
children = (
- 61A225910D1C3ADD00430CCD /* NSBundle+SUAdditions.h */,
- 61A225920D1C3ADD00430CCD /* NSBundle+SUAdditions.m */,
+ 61EF67580E25C5B400F754E0 /* SUHost.h */,
+ 61EF67550E25B58D00F754E0 /* SUHost.m */,
611779570D1111C400749C97 /* NSWorkspace_RBAdditions.h */,
611779560D1111C400749C97 /* NSWorkspace_RBAdditions.m */,
610134750DD252E60049ACDF /* NSWorkspace+SystemVersion.h */,
@@ -482,8 +478,6 @@
61299B3509CB04E000B7442F /* Sparkle.h */,
61299A5B09CA6D4500B7442F /* SUConstants.h */,
61299A5F09CA6EB100B7442F /* SUConstants.m */,
- 61A226290D1C58FD00430CCD /* SUUserDefaults.h */,
- 61A2262A0D1C58FD00430CCD /* SUUserDefaults.m */,
);
includeInIndex = 1;
name = "Other Sources";
@@ -563,10 +557,8 @@
61299B3609CB04E000B7442F /* Sparkle.h in Headers */,
6120721209CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h in Headers */,
611779590D1111C900749C97 /* NSWorkspace_RBAdditions.h in Headers */,
- 61A225930D1C3ADD00430CCD /* NSBundle+SUAdditions.h in Headers */,
61A2259E0D1C495D00430CCD /* SUVersionComparisonProtocol.h in Headers */,
61A225A40D1C4AC000430CCD /* SUStandardVersionComparator.h in Headers */,
- 61A2262B0D1C58FD00430CCD /* SUUserDefaults.h in Headers */,
61A2279C0D1CEE7600430CCD /* SUSystemProfiler.h in Headers */,
6160E7E10D3B4A8800E9CD71 /* NTSynchronousTask.h in Headers */,
612DCBAF0D488BC60015DBEA /* SUUpdatePermissionPrompt.h in Headers */,
@@ -588,6 +580,7 @@
6102FE460E077FCE00F85D09 /* SUPipedUnarchiver.h in Headers */,
6102FE4A0E07803800F85D09 /* SUDiskImageUnarchiver.h in Headers */,
6102FE5B0E08C7EC00F85D09 /* SUUnarchiver_Private.h in Headers */,
+ 61EF67590E25C5B400F754E0 /* SUHost.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -824,9 +817,7 @@
6120721309CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m in Sources */,
610EC1E00CF3A5FE00AE239E /* NTSynchronousTask.m in Sources */,
6117795A0D1111C900749C97 /* NSWorkspace_RBAdditions.m in Sources */,
- 61A225940D1C3ADD00430CCD /* NSBundle+SUAdditions.m in Sources */,
61A225A50D1C4AC000430CCD /* SUStandardVersionComparator.m in Sources */,
- 61A2262C0D1C58FD00430CCD /* SUUserDefaults.m in Sources */,
61A2279D0D1CEE7600430CCD /* SUSystemProfiler.m in Sources */,
612DCBB00D488BC60015DBEA /* SUUpdatePermissionPrompt.m in Sources */,
6171D9080D57B81800BFE886 /* NSFileManager+Aliases.m in Sources */,
@@ -846,6 +837,7 @@
6102FE4B0E07803800F85D09 /* SUDiskImageUnarchiver.m in Sources */,
6102FE5C0E08C7EC00F85D09 /* SUUnarchiver_Private.m in Sources */,
61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */,
+ 61EF67560E25B58D00F754E0 /* SUHost.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1041,7 +1033,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(NATIVE_ARCH)";
+ ARCHS = "$(NATIVE_ARCH_ACTUAL)";
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_DYNAMIC_NO_PIC = NO;
Please sign in to comment.
Something went wrong with that request. Please try again.