Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Beware of falling bricks! Huge refactoring commit #1: cleansing Spark…

…le of the plague that was SUUtilities.
  • Loading branch information...
commit 63011aaefd6e9f2306599a4dff1aa437474d9d84 1 parent 535416a
andym authored
View
1  NSApplication+AppCopies.m
@@ -7,7 +7,6 @@
//
#import "NSApplication+AppCopies.h"
-#import "SUUtilities.h"
@implementation NSApplication (SUAppCopies)
View
45 NSBundle+SUAdditions.h
@@ -0,0 +1,45 @@
+//
+// NSBundle+SUAdditions.h
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 Andy Matuschak. All rights reserved.
+//
+
+#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;
+@end
View
58 NSBundle+SUAdditions.m
@@ -0,0 +1,58 @@
+//
+// NSBundle+SUAdditions.m
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 Andy Matuschak. All rights reserved.
+//
+
+#import "NSBundle+SUAdditions.h"
+#import "NSWorkspace_RBAdditions.h"
+
+@implementation NSBundle (SUAdditions)
+
+- (NSString *)name
+{
+ NSString *name = [self objectForInfoDictionaryKey:@"CFBundleDisplayName"];
+ if (name)
+ return name;
+ else
+ return [[[NSFileManager defaultManager] displayNameAtPath:[self bundlePath]] stringByDeletingPathExtension];
+}
+
+- (NSString *)version
+{
+ return [self objectForInfoDictionaryKey:@"CFBundleVersion"];
+}
+
+- (NSString *)displayVersion
+{
+ NSString *shortVersionString = [self objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ if (shortVersionString)
+ {
+ if ([shortVersionString isEqualToString:[self version]])
+ return shortVersionString;
+ else
+ return [shortVersionString stringByAppendingFormat:@" (%@)", [self version]];
+ }
+ 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"];
+ NSImage *icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
+ if (icon)
+ return icon;
+ else // Use a default icon if none is defined.
+ return [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
+}
+
+- (BOOL)isRunningFromDiskImage
+{
+ return [[[NSWorkspace sharedWorkspace] propertiesForPath:[self bundlePath]] objectForKey:NSWorkspace_RBimagefilepath] != nil;
+}
+
+@end
View
1  NSFileManager+Verification.m
@@ -9,7 +9,6 @@
// DSA stuff adapted from code provided by Allan Odgaard. Thanks, Allan!
#import "NSFileManager+Verification.h"
-#import "SUUtilities.h"
#import "md5.h"
#import <stdio.h>
View
7 SUAppcast.h
@@ -8,15 +8,12 @@
#import <Cocoa/Cocoa.h>
-@class RSS, SUAppcastItem, SUUtilities;
+@class RSS, SUAppcastItem;
@interface SUAppcast : NSObject {
NSArray *items;
id delegate;
- SUUtilities *utilities;
}
-- (id)initWithUtilities:(SUUtilities *)aUtility;
-
- (void)fetchAppcastFromURL:(NSURL *)url;
- (void)setDelegate:delegate;
@@ -27,4 +24,6 @@
@interface NSObject (SUAppcastDelegate)
- (void)appcastDidFinishLoading:(SUAppcast *)appcast;
+- (void)appcastDidFailToLoad:(SUAppcast *)appcast;
+- (NSString *)userAgentForAppcast:(SUAppcast *)appcast;
@end
View
15 SUAppcast.m
@@ -8,20 +8,10 @@
#import "SUAppcast.h"
#import "SUAppcastItem.h"
-#import "SUUtilities.h"
#import "RSS.h"
@implementation SUAppcast
-- (id)initWithUtilities:(SUUtilities *)aUtility
-{
- self = [super init];
- if (self != nil) {
- utilities = [aUtility retain];
- }
- return self;
-}
-
- (void)fetchAppcastFromURL:(NSURL *)url
{
[NSThread detachNewThreadSelector:@selector(_fetchAppcastFromURL:) toTarget:self withObject:url]; // let's not block the main thread
@@ -34,7 +24,6 @@ - (void)setDelegate:del
- (void)dealloc
{
- [utilities release];
[items release];
[super dealloc];
}
@@ -56,7 +45,9 @@ - (void)_fetchAppcastFromURL:(NSURL *)url
RSS *feed = [RSS alloc];
@try
{
- NSString *userAgent = [NSString stringWithFormat: @"%@/%@ (Mac OS X) Sparkle/1.5b1", [utilities hostAppName], [utilities hostAppVersion]];
+ NSString *userAgent = nil;
+ if ([delegate respondsToSelector:@selector(userAgentForAppcast:)])
+ userAgent = [delegate userAgentForAppcast:self];
feed = [feed initWithURL:url normalize:YES userAgent:userAgent];
if (!feed)
View
6 SUAutomaticUpdateAlert.h
@@ -8,13 +8,13 @@
#import <Cocoa/Cocoa.h>
-@class SUAppcastItem, SUUtilities;
+@class SUAppcastItem;
@interface SUAutomaticUpdateAlert : NSWindowController {
SUAppcastItem *updateItem;
- SUUtilities *utilities;
+ NSBundle *hostBundle;
}
-- initWithAppcastItem:(SUAppcastItem *)item andUtilities:(SUUtilities *)aUtility;;
+- initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle;
- (IBAction)relaunchNow:sender;
- (IBAction)relaunchLater:sender;
View
23 SUAutomaticUpdateAlert.m
@@ -7,26 +7,25 @@
//
#import "SUAutomaticUpdateAlert.h"
-#import "SUUtilities.h"
+#import "NSBundle+SUAdditions.h"
#import "SUAppcastItem.h"
@implementation SUAutomaticUpdateAlert
-- initWithAppcastItem:(SUAppcastItem *)item andUtilities:(SUUtilities *)aUtility;
+- initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb;
{
+ updateItem = [item retain];
+ hostBundle = [hb retain];
+
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"SUAutomaticUpdateAlert" ofType:@"nib"];
- if (!path) // slight hack to resolve issues with running with in configurations
+ if (path == nil) // Slight hack to resolve issues with running Sparkle in debug configurations.
{
- NSBundle *current = [NSBundle bundleForClass:[self class]];
- NSString *frameworkPath = [[[NSBundle mainBundle] sharedFrameworksPath] stringByAppendingFormat:@"/Sparkle.framework", [current bundleIdentifier]];
+ NSString *frameworkPath = [[hostBundle sharedFrameworksPath] stringByAppendingString:@"/Sparkle.framework"];
NSBundle *framework = [NSBundle bundleWithPath:frameworkPath];
path = [framework pathForResource:@"SUAutomaticUpdateAlert" ofType:@"nib"];
}
[super initWithWindowNibPath:path owner:self];
-
- updateItem = [item retain];
- utilities = [aUtility retain];
[self setShouldCascadeWindows:NO];
return self;
@@ -34,7 +33,7 @@ @implementation SUAutomaticUpdateAlert
- (void) dealloc
{
- [utilities release];
+ [hostBundle release];
[updateItem release];
[super dealloc];
}
@@ -54,17 +53,17 @@ - (IBAction)relaunchLater:sender
- (NSImage *)applicationIcon
{
- return [utilities hostAppIcon];
+ return [hostBundle icon];
}
- (NSString *)titleText
{
- return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ has been installed!", nil), [utilities hostAppDisplayName]];
+ return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ has been installed!", nil), [hostBundle name]];
}
- (NSString *)descriptionText
{
- return [NSString stringWithFormat:SULocalizedString(@"%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?", nil), [utilities hostAppDisplayName], [updateItem versionString], [utilities hostAppDisplayName]];
+ return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been installed and will be ready to use next time %1$@ starts! Would you like to relaunch now?", nil), [hostBundle name], [hostBundle displayVersion]];
}
@end
View
39 SUBundleDefaults.h
@@ -1,39 +0,0 @@
-//
-// SUBundleDefaults.h
-// Sparkle
-//
-// Created by Christopher Atlan on 07.11.07.
-// Copyright 2007 Christopher Atlan. All rights reserved.
-//
-
-#import <Cocoa/Cocoa.h>
-
-@class SUUtilities;
-@interface SUBundleDefaults : NSObject {
- SUUtilities *utilities;
- NSString *applicationID;
-}
-
-- (id)initWithUtilitie:(SUUtilities *)theUtilities;
-
-- (id)objectForKey:(NSString *)defaultName;
-- (void)setObject:(id)value forKey:(NSString *)defaultName;
-- (void)removeObjectForKey:(NSString *)defaultName;
-
-- (NSString *)stringForKey:(NSString *)defaultName;
-- (NSArray *)arrayForKey:(NSString *)defaultName;
-- (NSDictionary *)dictionaryForKey:(NSString *)defaultName;
-- (NSData *)dataForKey:(NSString *)defaultName;
-- (NSArray *)stringArrayForKey:(NSString *)defaultName;
-/*
-- (int)integerForKey:(NSString *)defaultName;
-- (float)floatForKey:(NSString *)defaultName;
-*/
-- (BOOL)boolForKey:(NSString *)defaultName;
-/*
-- (void)setInteger:(int)value forKey:(NSString *)defaultName;
-- (void)setFloat:(float)value forKey:(NSString *)defaultName;
-*/
-- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
-
-@end
View
98 SUBundleDefaults.m
@@ -1,98 +0,0 @@
-//
-// SUBundleDefaults.m
-// Sparkle
-//
-// Created by Christopher Atlan on 07.11.07.
-// Copyright 2007 __MyCompanyName__. All rights reserved.
-//
-
-#import "SUBundleDefaults.h"
-#import "SUUtilities.h"
-
-@implementation SUBundleDefaults
-
-- (id)initWithUtilitie:(SUUtilities *)theUtilities
-{
- self = [super init];
- if (self != nil) {
- utilities = [theUtilities retain];
- applicationID = [utilities hostAppID];
- }
- return self;
-}
-
-- (void) dealloc
-{
- [utilities release];
- [applicationID release];
- [super dealloc];
-}
-
-
-- (id)objectForKey:(NSString *)defaultName
-{
- return (id)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (void)setObject:(id)value forKey:(NSString *)defaultName;
-{
- CFPreferencesSetValue((CFStringRef)defaultName, value, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- CFPreferencesSynchronize((CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (void)removeObjectForKey:(NSString *)defaultName
-{
- CFPreferencesSetValue((CFStringRef)defaultName, NULL, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- CFPreferencesSynchronize((CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (NSString *)stringForKey:(NSString *)defaultName
-{
- return (NSString *)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (NSArray *)arrayForKey:(NSString *)defaultName
-{
- return (NSArray *)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (NSDictionary *)dictionaryForKey:(NSString *)defaultName
-{
- return (NSDictionary *)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (NSData *)dataForKey:(NSString *)defaultName
-{
- return (NSData *)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-- (NSArray *)stringArrayForKey:(NSString *)defaultName
-{
- return (NSArray *)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-/*
-- (int)integerForKey:(NSString *)defaultName;
-- (float)floatForKey:(NSString *)defaultName;
-*/
-
-- (BOOL)boolForKey:(NSString *)defaultName
-{
- CFPropertyListRef plr = (CFPropertyListRef)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- if (plr == NULL)
- return NO;
- return CFBooleanGetValue((CFBooleanRef)plr);
-}
-
-/*
-- (void)setInteger:(int)value forKey:(NSString *)defaultName;
-- (void)setFloat:(float)value forKey:(NSString *)defaultName;
-*/
-
-- (void)setBool:(BOOL)value forKey:(NSString *)defaultName
-{
- CFPreferencesSetValue((CFStringRef)defaultName, (CFBooleanRef)[NSNumber numberWithBool:value], (CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- CFPreferencesSynchronize((CFStringRef)applicationID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
-}
-
-@end
View
31 SUStandardVersionComparator.h
@@ -0,0 +1,31 @@
+//
+// SUStandardVersionComparator.h
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "SUVersionComparisonProtocol.h"
+
+/*!
+ @class
+ @abstract Sparkle's default version comparator.
+ @discussion This comparator is adapted from MacPAD, by Kevin Ballard. It's "dumb" in that it does essentially string comparison, in components split by character type.
+*/
+@interface SUStandardVersionComparator : NSObject <SUVersionComparison> { }
+
+/*!
+ @method
+ @abstract Returns a singleton instance of the comparator.
+*/
++ (SUStandardVersionComparator *)defaultComparator;
+
+/*!
+ @method
+ @abstract Compares version strings through textual analysis.
+ @discussion See the implementation for more details.
+*/
+- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB;
+@end
View
180 SUUtilities.m → SUStandardVersionComparator.m
@@ -1,141 +1,30 @@
//
-// SUUtilities.m
+// SUStandardVersionComparator.m
// Sparkle
//
-// Created by Andy Matuschak on 3/12/06.
-// Copyright 2006 Andy Matuschak. All rights reserved.
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 __MyCompanyName__. All rights reserved.
//
-#import "SUUtilities.h"
-#import "SUUpdater.h"
-#import "SUBundleDefaults.h"
-#import "NSWorkspace_RBAdditions.h"
+#import "SUStandardVersionComparator.h"
-@implementation SUUtilities
+@implementation SUStandardVersionComparator
-+ (NSString *)localizedStringForKey:(NSString *)key withComment:(NSString *)comment
++ (SUStandardVersionComparator *)defaultComparator
{
- return NSLocalizedStringFromTableInBundle(key, @"Sparkle", [NSBundle bundleForClass:[self class]], comment);
+ static SUStandardVersionComparator *defaultComparator = nil;
+ if (defaultComparator == nil)
+ defaultComparator = [[SUStandardVersionComparator alloc] init];
+ return defaultComparator;
}
-- (id)initWithUpdater:(SUUpdater *)aUpdater
-{
- self = [super init];
- if (self != nil) {
- updater = [aUpdater retain];
- defaults = [[SUBundleDefaults alloc] initWithUtilitie:self];
- }
- return self;
-}
-
-- (void) dealloc
-{
- [updater release];
- [defaults release];
-
- [super dealloc];
-}
-
-- (id)unlocalizedInfoValueForKey:(NSString *)key
-{
- return [[[updater updateBundle] infoDictionary] objectForKey:key];
-}
-
-- (id)infoValueForKey:(NSString *)key
-{
- return [[updater updateBundle] objectForInfoDictionaryKey:key];
-}
-
-- (NSString *)hostAppName
-{
- if ([self infoValueForKey:@"CFBundleName"]) { return [self infoValueForKey:@"CFBundleName"]; }
- return [[[NSFileManager defaultManager] displayNameAtPath:[[updater updateBundle] bundlePath]] stringByDeletingPathExtension];
-}
-
-- (NSString *)hostAppDisplayName
-{
- if ([self infoValueForKey:@"CFBundleDisplayName"]) { return [self infoValueForKey:@"CFBundleDisplayName"]; }
- return [self hostAppName];
-}
-
-- (NSString *)hostAppVersion
-{
- return [self infoValueForKey:@"CFBundleVersion"];
-}
-
-- (NSString *)hostAppPath
-{
- return [[updater updateBundle] bundlePath] ;
-}
-
-- (NSString *)hostAppVersionString
-{
- NSString *shortVersionString = [self infoValueForKey:@"CFBundleShortVersionString"];
- if (shortVersionString)
- {
- if (![shortVersionString isEqualToString:[self hostAppVersion]])
- shortVersionString = [shortVersionString stringByAppendingFormat:@"/%@", [self hostAppVersion]];
- return shortVersionString;
- }
- else
- return [self hostAppVersion]; // fall back on CFBundleVersion
-}
-
-- (NSString *)hostAppID
-{
- return [self unlocalizedInfoValueForKey:@"CFBundleIdentifier"];
-}
-
-- (NSImage *)hostAppIcon
-{
- // draw the app's icon
- NSImage* iconImage = nil;
- NSString* iconFileStr = [[[updater updateBundle] infoDictionary] objectForKey:@"CFBundleIconFile"];
- if (iconFileStr != nil && [iconFileStr length] > 0)
- iconFileStr = [[updater updateBundle] pathForResource:iconFileStr ofType:@"icns"];
- if (iconFileStr != nil && [iconFileStr length] > 0)
-
- {
- // we have a real icon
- iconImage = [[NSImage alloc] initWithContentsOfFile:iconFileStr];
- }
- else
- {
- // no particular app icon defined, use the default system icon
- iconImage = [NSImage imageNamed: @"NSApplicationIcon"];
- // or
- //NSString* appIconType = NSFileTypeForHFSTypeCode(kGenericApplicationIcon);
- //iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:appIconType];
- }
- return iconImage;
-}
-
-- (NSString *)hostAppExtension
-{
- return [[[updater updateBundle] bundlePath] pathExtension];
-}
-
-- (SUBundleDefaults *)standardBundleDefaults
-{
- return [[defaults retain] autorelease];
-}
-
-- (BOOL)isRunningFromDiskImage
-{
- return [[[NSWorkspace sharedWorkspace] propertiesForPath:[self hostAppPath]] objectForKey:NSWorkspace_RBimagefilepath] != nil;
-}
-
-@end
-
-enum {
+typedef enum {
kNumberType,
kStringType,
kPeriodType
-};
-
-// The version comparison code here is courtesy of Kevin Ballard, adapted from MacPAD. Thanks, Kevin!
+} SUCharacterType;
-int SUGetCharType(NSString *character)
+- (SUCharacterType)typeOfCharacter:(NSString *)character
{
if ([character isEqualToString:@"."]) {
return kPeriodType;
@@ -146,7 +35,7 @@ int SUGetCharType(NSString *character)
}
}
-NSArray *SUSplitVersionString(NSString *version)
+- (NSArray *)splitVersionString:(NSString *)version
{
NSString *character;
NSMutableString *s;
@@ -157,11 +46,11 @@ int SUGetCharType(NSString *character)
return parts;
}
s = [[[version substringToIndex:1] mutableCopy] autorelease];
- oldType = SUGetCharType(s);
+ oldType = [self typeOfCharacter:s];
n = [version length] - 1;
for (i = 1; i <= n; ++i) {
character = [version substringWithRange:NSMakeRange(i, 1)];
- newType = SUGetCharType(character);
+ newType = [self typeOfCharacter:character];
if (oldType != newType || oldType == kPeriodType) {
// We've reached a new segment
NSString *aPart = [[NSString alloc] initWithString:s];
@@ -180,10 +69,10 @@ int SUGetCharType(NSString *character)
return parts;
}
-NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *versionB)
+- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB;
{
- NSArray *partsA = SUSplitVersionString(versionA);
- NSArray *partsB = SUSplitVersionString(versionB);
+ NSArray *partsA = [self splitVersionString:versionA];
+ NSArray *partsB = [self splitVersionString:versionB];
NSString *partA, *partB;
NSInteger i, n, typeA, typeB, intA, intB;
@@ -193,8 +82,8 @@ NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *ver
partA = [partsA objectAtIndex:i];
partB = [partsB objectAtIndex:i];
- typeA = SUGetCharType(partA);
- typeB = SUGetCharType(partB);
+ typeA = [self typeOfCharacter:partA];
+ typeB = [self typeOfCharacter:partB];
// Compare types
if (typeA == typeB) {
@@ -203,9 +92,9 @@ NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *ver
intA = [partA intValue];
intB = [partB intValue];
if (intA > intB) {
- return NSOrderedAscending;
- } else if (intA < intB) {
return NSOrderedDescending;
+ } else if (intA < intB) {
+ return NSOrderedAscending;
}
} else if (typeA == kStringType) {
NSComparisonResult result = [partB compare:partA];
@@ -217,16 +106,16 @@ NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *ver
// Not the same type? Now we have to do some validity checking
if (typeA != kStringType && typeB == kStringType) {
// typeA wins
- return NSOrderedAscending;
+ return NSOrderedDescending;
} else if (typeA == kStringType && typeB != kStringType) {
// typeB wins
- return NSOrderedDescending;
+ return NSOrderedAscending;
} else {
// One is a number and the other is a period. The period is invalid
if (typeA == kNumberType) {
- return NSOrderedAscending;
- } else {
return NSOrderedDescending;
+ } else {
+ return NSOrderedAscending;
}
}
}
@@ -235,21 +124,21 @@ NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *ver
// Lets check to see if one is larger than the other
if ([partsA count] != [partsB count]) {
// Yep. Lets get the next part of the larger
- // n holds the value we want
+ // n holds the index of the part we want.
NSString *missingPart;
int missingType, shorterResult, largerResult;
if ([partsA count] > [partsB count]) {
missingPart = [partsA objectAtIndex:n];
- shorterResult = NSOrderedDescending;
- largerResult = NSOrderedAscending;
- } else {
- missingPart = [partsB objectAtIndex:n];
shorterResult = NSOrderedAscending;
largerResult = NSOrderedDescending;
+ } else {
+ missingPart = [partsB objectAtIndex:n];
+ shorterResult = NSOrderedDescending;
+ largerResult = NSOrderedAscending;
}
- missingType = SUGetCharType(missingPart);
+ missingType = [self typeOfCharacter:missingPart];
// Check the type
if (missingType == kStringType) {
// It's a string. Shorter version wins
@@ -263,3 +152,6 @@ NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *ver
// The 2 strings are identical
return NSOrderedSame;
}
+
+
+@end
View
48 SUStatus.nib/classes.nib
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>CLASS</key>
+ <string>NSApplication</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>FirstResponder</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSObject</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>SUStatusController</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>actionButton</key>
+ <string>NSButton</string>
+ <key>progressBar</key>
+ <string>NSProgressIndicator</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSWindowController</string>
+ </dict>
+ </array>
+ <key>IBVersion</key>
+ <string>1</string>
+</dict>
+</plist>
View
20 SUStatus.nib/info.nib
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBFramework Version</key>
+ <string>629</string>
+ <key>IBLastKnownRelativeProjectPath</key>
+ <string>../Sparkle.xcodeproj</string>
+ <key>IBOldestOS</key>
+ <integer>5</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>6</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>9B18</string>
+ <key>targetFramework</key>
+ <string>IBCocoaFramework</string>
+</dict>
+</plist>
View
BIN  SUStatus.nib/keyedobjects.nib
Binary file not shown
View
7 SUStatusController.h
@@ -8,16 +8,15 @@
#import <Cocoa/Cocoa.h>
-@class SUUtilities;
@interface SUStatusController : NSWindowController {
- SUUtilities *utilities;
double progressValue, maxProgressValue;
NSString *title, *statusText, *buttonTitle;
IBOutlet NSButton *actionButton;
- IBOutlet NSProgressIndicator* mProgressBar;
+ IBOutlet NSProgressIndicator* progressBar;
+ NSBundle *hostBundle;
}
-- initWithUtilities:(SUUtilities *)aUtility;
+- initWithHostBundle:(NSBundle *)hostBundle;
// Pass 0 for the max progress value to get an indeterminate progress bar.
// Pass nil for the status text to not show it.
View
49 SUStatusController.m
@@ -7,39 +7,31 @@
//
#import "SUStatusController.h"
-#import "SUUtilities.h"
-
-@interface SUStatusController (Private)
-- (NSProgressIndicator *)progressBar;
-- (void)setProgressBar:(NSProgressIndicator *)theProgressBar;
-@end
+#import "NSBundle+SUAdditions.h"
@implementation SUStatusController
-- initWithUtilities:(SUUtilities *)aUtility
+- initWithHostBundle:(NSBundle *)hb
{
+ hostBundle = [hb retain];
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"SUStatus" ofType:@"nib"];
- if (!path) // slight hack to resolve issues with running in debug configurations
+ if (path == nil) // Slight hack to resolve issues with running Sparkle in debug configurations.
{
- NSBundle *current = [NSBundle bundleForClass:[self class]];
- NSString *frameworkPath = [[[NSBundle mainBundle] sharedFrameworksPath] stringByAppendingFormat:@"/Sparkle.framework", [current bundleIdentifier]];
+ NSString *frameworkPath = [[hostBundle sharedFrameworksPath] stringByAppendingString:@"/Sparkle.framework"];
NSBundle *framework = [NSBundle bundleWithPath:frameworkPath];
path = [framework pathForResource:@"SUStatus" ofType:@"nib"];
}
[super initWithWindowNibPath:path owner:self];
[self setShouldCascadeWindows:NO];
- utilities = [aUtility retain];
return self;
}
- (void)dealloc
{
- [utilities release];
+ [hostBundle release];
[title release];
[statusText release];
[buttonTitle release];
- [self setProgressBar:nil];
-
[super dealloc];
}
@@ -47,19 +39,17 @@ - (void)awakeFromNib
{
[[self window] center];
[[self window] setFrameAutosaveName:@"SUStatusFrame"];
-
- // set progress bar to threaded animation
- [[self progressBar] setUsesThreadedAnimation:YES];
+ [progressBar setUsesThreadedAnimation:YES];
}
- (NSString *)windowTitle
{
- return [NSString stringWithFormat:SULocalizedString(@"Updating %@", nil), [utilities hostAppDisplayName]];
+ return [NSString stringWithFormat:SULocalizedString(@"Updating %@", nil), [hostBundle name]];
}
- (NSImage *)applicationIcon
{
- return [utilities hostAppIcon];
+ return [hostBundle icon];
}
- (void)beginActionWithTitle:(NSString *)aTitle maxProgressValue:(double)aMaxProgressValue statusText:(NSString *)aStatusText
@@ -130,25 +120,4 @@ - (void)setStatusText:(NSString *)aStatusText
@end
-@implementation SUStatusController (Private)
-
-//----------------------------------------------------------
-// progressBar
-//----------------------------------------------------------
-- (NSProgressIndicator *)progressBar
-{
- return mProgressBar;
-}
-
-- (void)setProgressBar:(NSProgressIndicator *)theProgressBar
-{
- if (mProgressBar != theProgressBar)
- {
- [mProgressBar release];
- mProgressBar = [theProgressBar retain];
- }
-}
-
-@end
-
View
6 SUUpdateAlert.h
@@ -15,10 +15,10 @@ typedef enum
SUSkipThisVersionChoice
} SUUpdateAlertChoice;
-@class WebView, SUAppcastItem, SUUtilities;
+@class WebView, SUAppcastItem;
@interface SUUpdateAlert : NSWindowController {
SUAppcastItem *updateItem;
- SUUtilities *utilities;
+ NSBundle *hostBundle;
id delegate;
IBOutlet WebView *releaseNotesView;
@@ -27,7 +27,7 @@ typedef enum
BOOL webViewFinishedLoading;
}
-- initWithAppcastItem:(SUAppcastItem *)item andUtilities:(SUUtilities *)aUtility;
+- initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle;
- (void)setDelegate:delegate;
- (IBAction)installUpdate:sender;
View
37 SUUpdateAlert.m
@@ -8,35 +8,33 @@
#import "SUUpdateAlert.h"
#import "SUAppcastItem.h"
-#import "SUUtilities.h"
+#import "NSBundle+SUAdditions.h"
#import <WebKit/WebKit.h>
@implementation SUUpdateAlert
-- initWithAppcastItem:(SUAppcastItem *)item andUtilities:(SUUtilities *)aUtility
+- initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb
{
+ hostBundle = [hb retain];
+ updateItem = [item retain];
+
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"SUUpdateAlert" ofType:@"nib"];
- if (!path) // slight hack to resolve issues with running with in configurations
+ if (path == nil) // Slight hack to resolve issues with running Sparkle in debug configurations.
{
- NSBundle *current = [NSBundle bundleForClass:[self class]];
- NSString *frameworkPath = [[[NSBundle mainBundle] sharedFrameworksPath] stringByAppendingFormat:@"/Sparkle.framework", [current bundleIdentifier]];
+ NSString *frameworkPath = [[hostBundle sharedFrameworksPath] stringByAppendingString:@"/Sparkle.framework"];
NSBundle *framework = [NSBundle bundleWithPath:frameworkPath];
path = [framework pathForResource:@"SUUpdateAlert" ofType:@"nib"];
}
[super initWithWindowNibPath:path owner:self];
-
- updateItem = [item retain];
- utilities = [aUtility retain];
[self setShouldCascadeWindows:NO];
-
return self;
}
- (void)dealloc
{
[updateItem release];
- [utilities release];
+ [hostBundle release];
[super dealloc];
}
@@ -91,15 +89,18 @@ - (void)displayReleaseNotes
- (BOOL)showsReleaseNotes
{
- if (![utilities infoValueForKey:SUShowReleaseNotesKey]) { return YES; } // defaults to YES
- return [[utilities infoValueForKey:SUShowReleaseNotesKey] boolValue];
+ NSNumber *shouldShowReleaseNotes = [hostBundle objectForInfoDictionaryKey:SUShowReleaseNotesKey];
+ if (shouldShowReleaseNotes == nil)
+ return YES; // defaults to YES
+ else
+ return [shouldShowReleaseNotes boolValue];
}
- (BOOL)allowsAutomaticUpdates
{
- if ([utilities infoValueForKey:SUExpectsDSASignatureKey]) { return NO; } // automatic updating requires DSA-signed updates
- if ([utilities infoValueForKey:SUAllowsAutomaticUpdatesKey]) { return YES; } // defaults to YES
- return [[utilities infoValueForKey:SUAllowsAutomaticUpdatesKey] boolValue];
+ if ([hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey]) { return NO; } // automatic updating requires DSA-signed updates
+ if ([hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey]) { return YES; } // defaults to YES
+ return [[hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue];
}
- (void)awakeFromNib
@@ -145,17 +146,17 @@ - (BOOL)windowShouldClose:note
- (NSImage *)applicationIcon
{
- return [utilities hostAppIcon];
+ return [hostBundle icon];
}
- (NSString *)titleText
{
- return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [utilities hostAppDisplayName]];
+ return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [hostBundle name]];
}
- (NSString *)descriptionText
{
- return [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available (you have %@). Would you like to download it now?", nil), [utilities hostAppDisplayName], [updateItem versionString], [utilities hostAppVersionString]];
+ return [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available (you have %@). Would you like to download it now?", nil), [hostBundle name], [updateItem versionString], [hostBundle displayVersion]];
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:frame
View
11 SUUpdater.h
@@ -14,11 +14,7 @@
// .zip, .dmg, .tar, .tbz, .tgz archives are supported at this time.
-// By default, Sparkle offers to show the user the release notes of the build they'll be
-// getting, which it assumes are in the description (or body) field of the relevant RSS item.
-// Set SUShowReleaseNotes to <false/> in Info.plist to hide the button.
-
-@class SUAppcastItem, SUUpdateAlert, SUStatusController, SUUtilities;
+@class SUAppcastItem, SUUpdateAlert, SUStatusController;
@interface SUUpdater : NSObject {
SUAppcastItem *updateItem;
@@ -36,14 +32,11 @@
NSString *currentSystemVersion;
- NSBundle *updateBundle;
- SUUtilities *utilities;
+ NSBundle *hostBundle;
}
- (id)initWithBundle:(NSBundle *)bundle;
-- (NSBundle *)updateBundle;
-
// This IBAction is meant for a main menu item. Hook up any menu item to this action,
// and Sparkle will check for updates and report back its findings verbosely.
- (IBAction)checkForUpdates:sender;
View
214 SUUpdater.m
@@ -10,7 +10,10 @@
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUUnarchiver.h"
-#import "SUUtilities.h"
+#import "NSBundle+SUAdditions.h"
+#import "SUUserDefaults.h"
+#import "SUVersionComparisonProtocol.h"
+#import "SUStandardVersionComparator.h"
#import "SUUpdateAlert.h"
#import "SUAutomaticUpdateAlert.h"
@@ -39,11 +42,14 @@ @implementation SUUpdater
- init
{
+ return [self initWithBundle:[[NSBundle mainBundle] retain]];
+}
+
+- (id)initWithBundle:(NSBundle *)bundle
+{
self = [super init];
-
- updateBundle = [[NSBundle mainBundle] retain];
- utilities = [[SUUtilities alloc] initWithUpdater:self];
-
+ hostBundle = [bundle retain];
+ [[SUUserDefaults standardUserDefaults] setIdentifier:[hostBundle bundleIdentifier]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:@"NSApplicationDidFinishLaunchingNotification" object:NSApp];
// OS version (Apple recommends using SystemVersion.plist instead of Gestalt() here, don't ask me why).
@@ -52,35 +58,9 @@ @implementation SUUpdater
NSString *versionPlistPath = @"/System/Library/CoreServices/SystemVersion.plist";
//gets a version string of the form X.Y.Z
currentSystemVersion = [[[NSDictionary dictionaryWithContentsOfFile:versionPlistPath] objectForKey:@"ProductVersion"] retain];
-
- return self;
-}
-
-
-- (id)initWithBundle:(NSBundle *)bundle
-{
- self = [super init];
- if (self != nil) {
- updateBundle = [bundle retain];
- utilities = [[SUUtilities alloc] initWithUpdater:self];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:@"NSApplicationDidFinishLaunchingNotification" object:NSApp];
-
- // OS version (Apple recommends using SystemVersion.plist instead of Gestalt() here, don't ask me why).
- // This code *should* use NSSearchPathForDirectoriesInDomains(NSCoreServiceDirectory, NSSystemDomainMask, YES)
- // but that returns /Library/CoreServices for some reason
- NSString *versionPlistPath = @"/System/Library/CoreServices/SystemVersion.plist";
- //gets a version string of the form X.Y.Z
- currentSystemVersion = [[[NSDictionary dictionaryWithContentsOfFile:versionPlistPath] objectForKey:@"ProductVersion"] retain];
- }
return self;
}
-- (NSBundle *)updateBundle
-{
- return [[updateBundle retain] autorelease];
-}
-
- (void)scheduleCheckWithInterval:(NSTimeInterval)interval
{
if (checkTimer)
@@ -101,15 +81,15 @@ - (void)scheduleCheckWithIntervalObject:(NSNumber *)interval
- (void)applicationDidFinishLaunching:(NSNotification *)note
{
- if ([[utilities standardBundleDefaults] boolForKey:SUIgnoreChecksKey])
+ if ([[SUUserDefaults standardUserDefaults] boolForKey:SUIgnoreChecksKey])
return;
-
+
// If there's a scheduled interval, we see if it's been longer than that interval since the last
// check. If so, we perform a startup check; if not, we don't.
if ([self storedCheckInterval])
{
NSTimeInterval interval = [self storedCheckInterval];
- NSDate *lastCheck = [[utilities standardBundleDefaults] objectForKey:SULastCheckTimeKey];
+ NSDate *lastCheck = [[SUUserDefaults standardUserDefaults] objectForKey:SULastCheckTimeKey];
if (!lastCheck) { lastCheck = [NSDate distantPast]; }
NSTimeInterval intervalSinceCheck = [[NSDate date] timeIntervalSinceDate:lastCheck];
if (intervalSinceCheck < interval)
@@ -127,28 +107,28 @@ - (void)applicationDidFinishLaunching:(NSNotification *)note
else
{
// There's no scheduled check, so let's see if we're supposed to check on startup.
- NSNumber *shouldCheckAtStartup = [[utilities standardBundleDefaults] objectForKey:SUCheckAtStartupKey];
+ NSNumber *shouldCheckAtStartup = [[SUUserDefaults standardUserDefaults] objectForKey:SUCheckAtStartupKey];
if (!shouldCheckAtStartup) // hasn't been set yet; ask the user
{
// Let's see if there's a key in Info.plist for a default, though. We'll let that override the dialog if it's there.
- NSNumber *infoStartupValue = [utilities infoValueForKey:SUCheckAtStartupKey];
+ NSNumber *infoStartupValue = [hostBundle objectForInfoDictionaryKey:SUCheckAtStartupKey];
if (infoStartupValue)
{
shouldCheckAtStartup = infoStartupValue;
}
else
{
- NSImage *bundleIcon = [utilities hostAppIcon];
+ NSImage *bundleIcon = [hostBundle icon];
NSImage *appIcon = [NSImage imageNamed: @"NSApplicationIcon"];
if ([appIcon setName:@"NSApplicationIconWorkAround"])
[bundleIcon setName:@"NSApplicationIcon"];
-
- shouldCheckAtStartup = [NSNumber numberWithBool:NSRunAlertPanel(SULocalizedString(@"Check for updates on startup?", nil), [NSString stringWithFormat:SULocalizedString(@"Would you like %@ to check for updates on startup? If not, you can initiate the check manually from the %@ menu.", nil), [utilities hostAppDisplayName], [utilities hostAppDisplayName]], SULocalizedString(@"Yes", nil), SULocalizedString(@"No", nil), nil) == NSAlertDefaultReturn];
-
+
+ shouldCheckAtStartup = [NSNumber numberWithBool:NSRunAlertPanel(SULocalizedString(@"Check for updates on startup?", nil), [NSString stringWithFormat:SULocalizedString(@"Would you like %1$@ to check for updates on startup? If not, you can initiate the check manually from the %1$@ menu.", nil), [hostBundle name]], SULocalizedString(@"Yes", nil), SULocalizedString(@"No", nil), nil) == NSAlertDefaultReturn];
+
if ([bundleIcon setName:@"NSApplicationIconWorkAround2"])
[appIcon setName:@"NSApplicationIcon"];
}
- [[utilities standardBundleDefaults] setObject:shouldCheckAtStartup forKey:SUCheckAtStartupKey];
+ [[SUUserDefaults standardUserDefaults] setObject:shouldCheckAtStartup forKey:SUCheckAtStartupKey];
}
if ([shouldCheckAtStartup boolValue])
@@ -159,7 +139,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)note
- (void)dealloc
{
[updateItem release];
- [updateAlert release];
+ [updateAlert release];
[downloadPath release];
[statusController release];
@@ -171,12 +151,8 @@ - (void)dealloc
if (currentSystemVersion)
[currentSystemVersion release];
- if (updateBundle)
- [updateBundle release];
-
- if (utilities)
- [utilities release];
-
+ [hostBundle release];
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
@@ -192,8 +168,8 @@ - (IBAction)checkForUpdates:sender
}
- (BOOL)validateMenuItem:(NSMenuItem *)item {
- if ([item action] == @selector(checkForUpdates:)) {
- if (![[utilities standardBundleDefaults] boolForKey:SUIgnoreChecksKey])
+ if ([item action] == @selector(checkForUpdates:)) {
+ if (![[SUUserDefaults standardUserDefaults] boolForKey:SUIgnoreChecksKey])
{
if (updateInProgress)
return NO;
@@ -202,21 +178,21 @@ - (BOOL)validateMenuItem:(NSMenuItem *)item {
} else {
return NO;
}
- }
- return NO;
+ }
+ return NO;
}
// If the verbosity flag is YES, Sparkle will say when it can't reach the server and when there's no new update.
// This is generally useful for a menu item--when the check is explicitly invoked.
- (void)checkForUpdatesAndNotify:(BOOL)verbosity
{
- if ([[utilities standardBundleDefaults] boolForKey:SUIgnoreChecksKey])
+ if ([[SUUserDefaults standardUserDefaults] boolForKey:SUIgnoreChecksKey])
return;
- if ([utilities isRunningFromDiskImage])
+ if ([hostBundle isRunningFromDiskImage])
{
if (verbosity)
- [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a disk image. Move %1$@ to your Applications folder, relaunch it, and try again.", nil), [utilities hostAppName]]];
+ [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a disk image. Move %1$@ to your Applications folder, relaunch it, and try again.", nil), [hostBundle name]]];
return;
}
@@ -238,12 +214,12 @@ - (void)checkForUpdatesAndNotify:(BOOL)verbosity
updateInProgress = YES;
// 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 = [[utilities standardBundleDefaults] objectForKey:SUFeedURLKey];
+ NSString *appcastString = [[SUUserDefaults standardUserDefaults] objectForKey:SUFeedURLKey];
if (!appcastString)
- appcastString = [utilities infoValueForKey:SUFeedURLKey];
+ appcastString = [hostBundle objectForInfoDictionaryKey:SUFeedURLKey];
if (!appcastString) { [NSException raise:@"SUNoFeedURL" format:@"No feed URL is specified in the Info.plist or the user defaults!"]; }
- SUAppcast *appcast = [[SUAppcast alloc] initWithUtilities:utilities];
+ SUAppcast *appcast = [[SUAppcast alloc] init];
[appcast setDelegate:self];
[appcast fetchAppcastFromURL:[NSURL URLWithString:appcastString]];
}
@@ -251,15 +227,15 @@ - (void)checkForUpdatesAndNotify:(BOOL)verbosity
- (BOOL)automaticallyUpdates
{
// If the SUAllowsAutomaticUpdatesKey exists and is set to NO, return NO.
- if ([[utilities infoValueForKey:SUAllowsAutomaticUpdatesKey] boolValue] == NO && [utilities infoValueForKey:SUAllowsAutomaticUpdatesKey]) { return NO; }
+ if ([[hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] boolValue] == NO && [hostBundle objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey]) { return NO; }
// If we're not using DSA signatures, we aren't going to trust any updates automatically.
- if (![[utilities infoValueForKey:SUExpectsDSASignatureKey] boolValue]) { return NO; }
+ if (![[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue]) { return NO; }
// If there's no setting, we default to NO.
- if (![[utilities standardBundleDefaults] objectForKey:SUAutomaticallyUpdateKey]) { return NO; }
+ if (![[SUUserDefaults standardUserDefaults] objectForKey:SUAutomaticallyUpdateKey]) { return NO; }
- return [[[utilities standardBundleDefaults] objectForKey:SUAutomaticallyUpdateKey] boolValue];
+ return [[[SUUserDefaults standardUserDefaults] objectForKey:SUAutomaticallyUpdateKey] boolValue];
}
- (BOOL)isAutomaticallyUpdating
@@ -271,11 +247,11 @@ - (void)showUpdateErrorAlertWithInfo:(NSString *)info
{
if ([self isAutomaticallyUpdating]) { return; }
- NSImage *bundleIcon = [utilities hostAppIcon];
+ NSImage *bundleIcon = [hostBundle icon];
NSImage *appIcon = [NSImage imageNamed: @"NSApplicationIcon"];
if ([appIcon setName:@"NSApplicationIconWorkAround"])
[bundleIcon setName:@"NSApplicationIcon"];
-
+
NSRunAlertPanel(SULocalizedString(@"Update Error!", nil), info, SULocalizedString(@"Cancel", nil), nil, nil);
if ([bundleIcon setName:@"NSApplicationIconWorkAround2"])
@@ -286,20 +262,20 @@ - (NSTimeInterval)storedCheckInterval
{
// Define some minimum intervals to avoid DOS-like checking attacks.
#ifdef DEBUG
- #define MIN_INTERVAL 60
+#define MIN_INTERVAL 60
#else
- #define MIN_INTERVAL 60*60
+#define MIN_INTERVAL 60*60
#endif
// Returns the scheduled check interval stored in the user defaults / info.plist. User defaults override Info.plist.
long interval = 0; // 0 signifies not to do timed checking.
- if ([[utilities standardBundleDefaults] objectForKey:SUScheduledCheckIntervalKey])
+ if ([[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey])
{
- interval = [[[utilities standardBundleDefaults] objectForKey:SUScheduledCheckIntervalKey] longValue];
+ interval = [[[SUUserDefaults standardUserDefaults] objectForKey:SUScheduledCheckIntervalKey] longValue];
}
- else if ([utilities infoValueForKey:SUScheduledCheckIntervalKey])
+ else if ([hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey])
{
- interval = [[utilities infoValueForKey:SUScheduledCheckIntervalKey] longValue];
+ interval = [[hostBundle objectForInfoDictionaryKey:SUScheduledCheckIntervalKey] longValue];
}
if (interval >= MIN_INTERVAL)
return interval;
@@ -311,7 +287,7 @@ - (void)beginDownload
{
if (![self isAutomaticallyUpdating])
{
- statusController = [[SUStatusController alloc] initWithUtilities:utilities];
+ statusController = [[SUStatusController alloc] initWithHostBundle:hostBundle];
[statusController beginActionWithTitle:SULocalizedString(@"Downloading update...", nil) maxProgressValue:0 statusText:nil];
[statusController setButtonTitle:SULocalizedString(@"Cancel", nil) target:self action:@selector(cancelDownload:) isDefault:NO];
[statusController showWindow:self];
@@ -323,7 +299,7 @@ - (void)beginDownload
- (void)remindMeLater
{
// Clear out the skipped version so the dialog will actually come back if it was already skipped.
- [[utilities standardBundleDefaults] setObject:nil forKey:SUSkippedVersionKey];
+ [[SUUserDefaults standardUserDefaults] setObject:nil forKey:SUSkippedVersionKey];
if (checkInterval)
[self scheduleCheckWithInterval:checkInterval];
@@ -342,7 +318,7 @@ - (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoi
{
case SUInstallUpdateChoice:
// Clear out the skipped version so the dialog will come back if the download fails.
- [[utilities standardBundleDefaults] setObject:nil forKey:SUSkippedVersionKey];
+ [[SUUserDefaults standardUserDefaults] setObject:nil forKey:SUSkippedVersionKey];
[self beginDownload];
break;
@@ -353,7 +329,7 @@ - (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoi
case SUSkipThisVersionChoice:
updateInProgress = NO;
- [[utilities standardBundleDefaults] setObject:[updateItem fileVersion] forKey:SUSkippedVersionKey];
+ [[SUUserDefaults standardUserDefaults] setObject:[updateItem fileVersion] forKey:SUSkippedVersionKey];
if (checkInterval)
[self scheduleCheckWithInterval:checkInterval];
break;
@@ -362,7 +338,7 @@ - (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoi
- (void)showUpdatePanel
{
- updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:updateItem andUtilities:utilities];
+ updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:updateItem hostBundle:hostBundle];
[updateAlert setDelegate:self];
// Only show the update alert if the app is active; otherwise, we'll wait until it is.
@@ -372,6 +348,16 @@ - (void)showUpdatePanel
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:@"NSApplicationDidBecomeActiveNotification" object:NSApp];
}
+- (NSString *)systemVersionString
+{
+ return currentSystemVersion;
+}
+
+- (NSString *)userAgentForAppcast:(SUAppcast *)ac
+{
+ return [NSString stringWithFormat: @"%@/%@ Sparkle/1.5b1", [hostBundle name], [hostBundle displayVersion]];
+}
+
- (void)appcastDidFailToLoad:(SUAppcast *)ac
{
[ac autorelease];
@@ -383,36 +369,31 @@ - (void)appcastDidFailToLoad:(SUAppcast *)ac
// Override this to change the new version comparison logic!
- (BOOL)newVersionAvailable
{
- BOOL canRunOnCurrentSystem = (SUStandardVersionComparison([updateItem minimumSystemVersion], [self systemVersionString]) != NSOrderedAscending);
- return (canRunOnCurrentSystem && (SUStandardVersionComparison([updateItem fileVersion], [utilities hostAppVersion]) == NSOrderedAscending));
- // Want straight-up string comparison like Sparkle 1.0b3 and earlier? Uncomment the line below and comment the one above.
- // return ![SUHostAppVersion() isEqualToString:[updateItem fileVersion]];
-}
-
-- (NSString *)systemVersionString
-{
- return currentSystemVersion;
+ id <SUVersionComparison> comparator = [SUStandardVersionComparator defaultComparator];
+ BOOL canRunOnCurrentSystem = ([comparator compareVersion:[updateItem minimumSystemVersion] toVersion:[self systemVersionString]] != NSOrderedDescending);
+ return (canRunOnCurrentSystem && ([comparator compareVersion:[hostBundle version] toVersion:[updateItem fileVersion]]) == NSOrderedAscending);
}
- (void)appcastDidFinishLoading:(SUAppcast *)ac
{
@try
{
- if (!ac) { [NSException raise:@"SUAppcastException" format:@"Couldn't get a valid appcast from the server."]; }
-
+ if (!ac)
+ [NSException raise:@"SUAppcastException" format:@"Couldn't get a valid appcast from the server."];
+
updateItem = [[ac newestItem] retain];
[ac autorelease];
-
+
// Record the time of the check for host app use and for interval checks on startup.
- [[utilities standardBundleDefaults] setObject:[NSDate date] forKey:SULastCheckTimeKey];
-
+ [[SUUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:SULastCheckTimeKey];
+
if (![updateItem fileVersion])
{
[NSException raise:@"SUAppcastException" format:@"Can't extract a version string from the appcast feed. The filenames should look like YourApp_1.5.tgz, where 1.5 is the version number."];
}
-
- if (!verbose && [[[utilities standardBundleDefaults] objectForKey:SUSkippedVersionKey] isEqualToString:[updateItem fileVersion]]) { updateInProgress = NO; return; }
-
+
+ if (!verbose && [[[SUUserDefaults standardUserDefaults] objectForKey:SUSkippedVersionKey] isEqualToString:[updateItem fileVersion]]) { updateInProgress = NO; return; }
+
if ([self newVersionAvailable])
{
if (checkTimer) // There's a new version! Let's disable the automated checking timer unless the user cancels.
@@ -434,12 +415,12 @@ - (void)appcastDidFinishLoading:(SUAppcast *)ac
{
if (verbose) // We only notify on no new version when we're being verbose.
{
- NSImage *bundleIcon = [utilities hostAppIcon];
+ NSImage *bundleIcon = [hostBundle icon];
NSImage *appIcon = [NSImage imageNamed: @"NSApplicationIcon"];
if ([appIcon setName:@"NSApplicationIconWorkAround"])
[bundleIcon setName:@"NSApplicationIcon"];
-
- NSRunAlertPanel(SULocalizedString(@"You're up to date!", nil), [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [utilities hostAppDisplayName], [utilities hostAppVersionString]], SULocalizedString(@"OK", nil), nil, nil);
+
+ NSRunAlertPanel(SULocalizedString(@"You're up to date!", nil), [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [hostBundle name], [hostBundle displayVersion]], SULocalizedString(@"OK", nil), nil, nil);
if ([bundleIcon setName:@"NSApplicationIconWorkAround2"])
[appIcon setName:@"NSApplicationIcon"];
@@ -540,10 +521,10 @@ - (void)extractUpdate
}
// DSA verification, if activated by the developer
- if ([[utilities infoValueForKey:SUExpectsDSASignatureKey] boolValue])
+ if ([[hostBundle objectForInfoDictionaryKey:SUExpectsDSASignatureKey] boolValue])
{
NSString *dsaSignature = [updateItem DSASignature];
- NSString *pkeyString = [utilities infoValueForKey:SUPublicDSAKeyKey]; // Fetch the app's public DSA key.
+ NSString *pkeyString = [hostBundle objectForInfoDictionaryKey:SUPublicDSAKeyKey]; // Fetch the app's public DSA key.
if (![[NSFileManager defaultManager] validatePath:downloadPath withEncodedDSASignature:dsaSignature withPublicDSAKey:pkeyString])
{
[NSException raise:@"SUUnarchiveException" format:@"DSA verification of the update archive failed."];
@@ -585,11 +566,9 @@ - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
NSLog(@"Download error: %@", [error localizedDescription]);
[self showUpdateErrorAlertWithInfo:SULocalizedString(@"An error occurred while trying to download the file. Please try again later.", nil)];
}
-
+
- (IBAction)installAndRestart:sender
{
- NSString *currentAppPath = [utilities hostAppPath];
- NSString *currentBundlePath = [updateBundle bundlePath];
NSString *newAppDownloadPath = nil;
BOOL isPackage = NO;
int processIdentifier = [[NSProcessInfo processInfo] processIdentifier];
@@ -605,34 +584,29 @@ - (IBAction)installAndRestart:sender
while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]))
[NSApp sendEvent:event];
}
-
- // We assume that the archive will contain a file named {CFBundleName}.app
- // (where, obviously, CFBundleName comes from Info.plist)
- if (![utilities unlocalizedInfoValueForKey:@"CFBundleName"]) { [NSException raise:@"SUInstallException" format:@"This application has no CFBundleName! This key must be set to the application's name."]; }
-
+
// Search subdirectories for the application
- NSString *file, *appName = [utilities unlocalizedInfoValueForKey:@"CFBundleName"];
+ NSString *file, *bundleFileName = [[hostBundle bundlePath] lastPathComponent];
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:[downloadPath stringByDeletingLastPathComponent]];
while ((file = [dirEnum nextObject]))
{
// Some DMGs have symlinks into /Applications! That's no good!
if ([file isEqualToString:@"/Applications"])
[dirEnum skipDescendents];
- if ([[file pathExtension] isEqualToString:[utilities hostAppExtension]] &&
- [[[file stringByDeletingPathExtension] lastPathComponent] isEqualToString:appName]) // We found one!
+ if ([[file lastPathComponent] isEqualToString:bundleFileName]) // We found one!
{
isPackage = NO;
newAppDownloadPath = [[downloadPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:file];
break;
}
else if (([[file pathExtension] isEqualToString:@"pkg"] || [[file pathExtension] isEqualToString:@"mpkg"]) &&
- [[file stringByDeletingPathExtension] isEqualToString:appName])
+ [[file stringByDeletingPathExtension] isEqualToString:[[[hostBundle bundlePath] lastPathComponent] stringByDeletingPathExtension]])
{
isPackage = YES;
newAppDownloadPath = [[downloadPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:file];
break;
}
- if ([[file pathExtension] isEqualToString:[utilities hostAppExtension]] ||
+ if ([[file pathExtension] isEqualToString:[[hostBundle bundlePath] pathExtension]] ||
[[file pathExtension] isEqualToString:@"pkg"] ||
[[file pathExtension] isEqualToString:@"mpkg"]) // No point in looking in bundles.
{
@@ -642,7 +616,7 @@ - (IBAction)installAndRestart:sender
if (!newAppDownloadPath || ![[NSFileManager defaultManager] fileExistsAtPath:newAppDownloadPath])
{
- [NSException raise:@"SUInstallException" format:@"The update archive didn't contain an application with the proper name: %@. Remember, the updated app's file name must be identical to {CFBundleName}.{Extension}", [[utilities unlocalizedInfoValueForKey:@"CFBundleName"] stringByAppendingPathExtension:[utilities hostAppExtension]]];
+ [NSException raise:@"SUInstallException" format:@"The update archive didn't contain an application with the proper name: %@.", bundleFileName];
}
}
@catch(NSException *e)
@@ -658,8 +632,8 @@ - (IBAction)installAndRestart:sender
if (!isPackage)
{
NSInteger tag = 0;
- BOOL result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[currentBundlePath stringByDeletingLastPathComponent] destination:@"" files:[NSArray arrayWithObject:[currentBundlePath lastPathComponent]] tag:&tag];
- result &= [[NSFileManager defaultManager] movePath:newAppDownloadPath toPath:currentBundlePath handler:nil];
+ BOOL result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[[hostBundle bundlePath] stringByDeletingLastPathComponent] destination:@"" files:[NSArray arrayWithObject:[[hostBundle bundlePath] lastPathComponent]] tag:&tag];
+ result &= [[NSFileManager defaultManager] movePath:newAppDownloadPath toPath:[hostBundle bundlePath] handler:nil];
if (!result)
{
[self abandonUpdate];
@@ -685,19 +659,19 @@ - (IBAction)installAndRestart:sender
}
else
{
- if (![[NSFileManager defaultManager] copyPathWithAuthentication:newAppDownloadPath toPath:currentBundlePath])
+ if (![[NSFileManager defaultManager] copyPathWithAuthentication:newAppDownloadPath toPath:[hostBundle bundlePath]])
{
- [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help.", nil), [utilities hostAppDisplayName]]];
+ [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help.", nil), [hostBundle name]]];
[self abandonUpdate];
return;
}
}
}
-
+
// Prompt for permission to restart if we're automatically updating.
if ([self isAutomaticallyUpdating])
{
- SUAutomaticUpdateAlert *alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:updateItem andUtilities:utilities];
+ SUAutomaticUpdateAlert *alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:updateItem hostBundle:hostBundle];
if ([NSApp runModalForWindow:[alert window]] == NSAlertAlternateReturn)
{
[alert release];
@@ -713,7 +687,7 @@ - (IBAction)installAndRestart:sender
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self];
-
+
NSString *relaunchPath = [[[NSBundle bundleForClass:[self class]] executablePath] stringByDeletingLastPathComponent];
if (!relaunchPath) // slight hack to resolve issues with running within bundles
{
@@ -722,8 +696,8 @@ - (IBAction)installAndRestart:sender
relaunchPath = [[framework executablePath] stringByDeletingLastPathComponent];
}
relaunchPath = [relaunchPath stringByAppendingPathComponent:@"relaunch.app/Contents/MacOS/relaunch"];
-
- [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:currentAppPath, [NSString stringWithFormat:@"%d", processIdentifier], nil]];
+
+ [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:[hostBundle bundlePath], [NSString stringWithFormat:@"%d", processIdentifier], nil]];
[NSApp terminate:self];
}
View
38 SUUserDefaults.h
@@ -0,0 +1,38 @@
+//
+// SUUserDefaults.h
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.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;
+
+- objectForKey:(NSString *)defaultName;
+- (void)setObject:(id)value forKey:(NSString *)defaultName;
+- (BOOL)boolForKey:(NSString *)defaultName;
+- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
+@end
View
69 SUUserDefaults.m
@@ -0,0 +1,69 @@
+//
+// SUUserDefaults.m
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 __MyCompanyName__. All rights reserved.
+//
+
+#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
+{
+ identifier = [anIdentifier copy];
+}
+
+- (void)verifyIdentifier
+{
+ if (identifier == nil)
+ [NSException raise:@"SUUserDefaultsMissingIdentifier" format:@"You must set the SUUserDefaults identifier before using it."];
+}
+
+- objectForKey:(NSString *)defaultName
+{
+ [self verifyIdentifier];
+ return (id)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+}
+
+- (void)setObject:(id)value forKey:(NSString *)defaultName;
+{
+ [self verifyIdentifier];
+ CFPreferencesSetValue((CFStringRef)defaultName, value, (CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ CFPreferencesSynchronize((CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+}
+
+- (BOOL)boolForKey:(NSString *)defaultName
+{
+ [self verifyIdentifier];
+ CFPropertyListRef plr = (CFPropertyListRef)CFPreferencesCopyValue((CFStringRef)defaultName, (CFStringRef)identifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (plr == NULL)
+ return NO;
+ else
+ return CFBooleanGetValue((CFBooleanRef)plr);
+}
+
+- (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);
+}
+
+@end
View
38 SUUtilities.h
@@ -1,38 +0,0 @@
-//
-// SUUtilities.h
-// Sparkle
-//
-// Created by Andy Matuschak on 3/12/06.
-// Copyright 2006 Andy Matuschak. All rights reserved.
-//
-
-#import <Cocoa/Cocoa.h>
-#define SULocalizedString(key,comment) [SUUtilities localizedStringForKey:key withComment:comment]
-
-@class SUUpdater, SUBundleDefaults;
-@interface SUUtilities : NSObject {
- SUUpdater *updater;
- SUBundleDefaults *defaults;
-}
-
-+ (NSString *)localizedStringForKey:(NSString *)key withComment:(NSString *)comment;
-
-- (id)initWithUpdater:(SUUpdater *)aUpdater;
-- (id)unlocalizedInfoValueForKey:(NSString *)key;
-- (id)infoValueForKey:(NSString *)key;
-- (NSString *)hostAppName;
-- (NSString *)hostAppDisplayName;
-- (NSString *)hostAppVersion;
-- (NSString *)hostAppVersionString;
-- (NSString *)hostAppID;
-- (NSImage *)hostAppIcon;
-- (NSString *)hostAppPath;
-- (NSString *)hostAppExtension;
-- (BOOL)isRunningFromDiskImage;
-- (SUBundleDefaults *)standardBundleDefaults;
-@end
-
-NSComparisonResult SUStandardVersionComparison(NSString * versionA, NSString * versionB);
-
-// If running make localizable-strings for genstrings, ignore the error on this line.
-//NSString *SULocalizedString(NSString *key, NSString *comment);
View
24 SUVersionComparisonProtocol.h
@@ -0,0 +1,24 @@
+//
+// SUVersionComparisonProtocol.h
+// Sparkle
+//
+// Created by Andy Matuschak on 12/21/07.
+// Copyright 2007 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+/*!
+ @protocol
+ @abstract Implement this protocol to provide version comparison facilities for Sparkle.
+*/
+@protocol SUVersionComparison
+
+/*!
+ @method
+ @abstract An abstract method to compare two version strings.
+ @discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent.
+*/
+- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB;
+
+@end
View
3  Sparkle.h
@@ -7,7 +7,6 @@
//
#import "SUUpdater.h"
-#import "SUUtilities.h"
#import "SUConstants.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
@@ -16,7 +15,9 @@
#import "SUStatusController.h"
#import "SUUnarchiver.h"
#import "SUStatusChecker.h"
+#import "SUUserDefaults.h"
#import "NSApplication+AppCopies.h"
#import "NSFileManager+Authentication.h"
#import "NSFileManager+Verification.h"
+#import "NSBundle+SUAdditions.h"
View
44 Sparkle.xcodeproj/project.pbxproj
@@ -30,6 +30,13 @@
61407C390A4099050009F71F /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; };
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 */; };
+ 61A225940D1C3ADD00430CCD /* NSBundle+SUAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225920D1C3ADD00430CCD /* NSBundle+SUAdditions.m */; };
+ 61A2259E0D1C495D00430CCD /* SUVersionComparisonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */; };
+ 61A225A40D1C4AC000430CCD /* SUStandardVersionComparator.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */; };
+ 61A225A50D1C4AC000430CCD /* SUStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */; };
+ 61A2262B0D1C58FD00430CCD /* SUUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A226290D1C58FD00430CCD /* SUUserDefaults.h */; };
+ 61A2262C0D1C58FD00430CCD /* SUUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A2262A0D1C58FD00430CCD /* SUUserDefaults.m */; };
61AAE8280A321A7F00D8810D /* Sparkle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8220A321A7F00D8810D /* Sparkle.strings */; };
61AAE8290A321A8000D8810D /* SUAutomaticUpdateAlert.nib in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8240A321A7F00D8810D /* SUAutomaticUpdateAlert.nib */; };
61AAE82A0A321A8000D8810D /* SUUpdateAlert.nib in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8260A321A7F00D8810D /* SUUpdateAlert.nib */; };
@@ -49,8 +56,6 @@
61B5F93009C4CFDC00B25A18 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F92409C4CFC900B25A18 /* main.m */; };
61B5FBB709C4FAFF00B25A18 /* SUAppcast.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FB9509C4F04600B25A18 /* SUAppcast.m */; };
61B5FC0D09C4FC8200B25A18 /* SUAppcast.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FB9409C4F04600B25A18 /* SUAppcast.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61B5FC0E09C4FC8400B25A18 /* SUUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FBCB09C4FBAB00B25A18 /* SUUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61B5FC0F09C4FC8500B25A18 /* SUUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FBCC09C4FBAB00B25A18 /* SUUtilities.m */; };
61B5FC4C09C4FD5E00B25A18 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5FC3F09C4FD4000B25A18 /* WebKit.framework */; };
61B5FC6F09C51F4900B25A18 /* SUAppcastItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FC5409C5182000B25A18 /* SUAppcastItem.m */; };
61B5FC7009C51F4A00B25A18 /* SUAppcastItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FC5309C5182000B25A18 /* SUAppcastItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -58,8 +63,6 @@
61B5FCDF09C52A9F00B25A18 /* SUUpdateAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FCA009C5228F00B25A18 /* SUUpdateAlert.h */; settings = {ATTRIBUTES = (Public, ); }; };
61BBDF820A49220C00378739 /* Sparkle.icns in Resources */ = {isa = PBXBuildFile; fileRef = 61BBDF810A49220C00378739 /* Sparkle.icns */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
- D18A64E80CE2588900C49C71 /* SUBundleDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = D18A64E60CE2588900C49C71 /* SUBundleDefaults.h */; };
- D18A64E90CE2588900C49C71 /* SUBundleDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = D18A64E70CE2588900C49C71 /* SUBundleDefaults.m */; };
D1E42C370CE754C700F50EB9 /* relaunch.m in Sources */ = {isa = PBXBuildFile; fileRef = 613242130CD06CEF00106AA4 /* relaunch.m */; };
D1E42C3B0CE755A000F50EB9 /* relaunch.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = D1E42C2F0CE754AE00F50EB9 /* relaunch.app */; };
/* End PBXBuildFile section */
@@ -132,6 +135,13 @@
613242130CD06CEF00106AA4 /* relaunch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = relaunch.m; sourceTree = "<group>"; };
6196CFE309C71ADE000DC222 /* SUStatusController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUStatusController.h; sourceTree = "<group>"; };
6196CFE409C71ADE000DC222 /* SUStatusController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUStatusController.m; 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>"; };
61AAE8230A321A7F00D8810D /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Sparkle.strings; sourceTree = "<group>"; };
61AAE8250A321A7F00D8810D /* en */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = en; path = en.lproj/SUAutomaticUpdateAlert.nib; sourceTree = "<group>"; };
61AAE8270A321A7F00D8810D /* en */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = en; path = en.lproj/SUUpdateAlert.nib; sourceTree = "<group>"; };
@@ -227,8 +237,6 @@
61B5F92D09C4CFD800B25A18 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = "Test Application/English.lproj/MainMenu.nib"; sourceTree = "<group>"; };
61B5FB9409C4F04600B25A18 /* SUAppcast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAppcast.h; sourceTree = "<group>"; };
61B5FB9509C4F04600B25A18 /* SUAppcast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAppcast.m; sourceTree = "<group>"; };
- 61B5FBCB09C4FBAB00B25A18 /* SUUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUtilities.h; sourceTree = "<group>"; };
- 61B5FBCC09C4FBAB00B25A18 /* SUUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUtilities.m; sourceTree = "<group>"; };
61B5FC3F09C4FD4000B25A18 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = /System/Library/Frameworks/WebKit.framework; sourceTree = "<absolute>"; };
61B5FC5309C5182000B25A18 /* SUAppcastItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAppcastItem.h; sourceTree = "<group>"; };
61B5FC5409C5182000B25A18 /* SUAppcastItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAppcastItem.m; sourceTree = "<group>"; };
@@ -237,8 +245,6 @@
61BBDF810A49220C00378739 /* Sparkle.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Sparkle.icns; sourceTree = "<group>"; };
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* Sparkle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Sparkle.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- D18A64E60CE2588900C49C71 /* SUBundleDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBundleDefaults.h; sourceTree = "<group>"; };
- D18A64E70CE2588900C49C71 /* SUBundleDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBundleDefaults.m; sourceTree = "<group>"; };
D1E42C2F0CE754AE00F50EB9 /* relaunch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = relaunch.app; sourceTree = BUILT_PRODUCTS_DIR; };
D1E42C330CE754AE00F50EB9 /* Relaunch Tool-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Relaunch Tool-Info.plist"; sourceTree = "<group>"; };
D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
@@ -412,18 +418,21 @@
611779560D1111C400749C97 /* NSWorkspace_RBAdditions.m */,
610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */,
610EC1C00CF3914D00AE239E /* NTSynchronousTask.h */,
- D18A64E60CE2588900C49C71 /* SUBundleDefaults.h */,
- D18A64E70CE2588900C49C71 /* SUBundleDefaults.m */,
61B5F8DD09C4CE3C00B25A18 /* md5.c */,
61B5F8DE09C4CE3C00B25A18 /* md5.h */,
61B5F8DF09C4CE3C00B25A18 /* NSString+extras.h */,
61B5F8E009C4CE3C00B25A18 /* NSString+extras.m */,
61B5F8E109C4CE3C00B25A18 /* RSS.h */,
61B5F8E209C4CE3C00B25A18 /* RSS.m */,
- 61B5FBCB09C4FBAB00B25A18 /* SUUtilities.h */,
- 61B5FBCC09C4FBAB00B25A18 /* SUUtilities.m */,
61299A5B09CA6D4500B7442F /* SUConstants.h */,
61299A5F09CA6EB100B7442F /* SUConstants.m */,
+ 61A225910D1C3ADD00430CCD /* NSBundle+SUAdditions.h */,
+ 61A225920D1C3ADD00430CCD /* NSBundle+SUAdditions.m */,
+ 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */,
+ 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */,
+ 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */,
+ 61A226290D1C58FD00430CCD /* SUUserDefaults.h */,
+ 61A2262A0D1C58FD00430CCD /* SUUserDefaults.m */,
);
includeInIndex = 1;
name = Utilities;
@@ -453,7 +462,6 @@
61B5F8EB09C4CE3C00B25A18 /* RSS.h in Headers */,
61B5F8ED09C4CE3C00B25A18 /* SUUpdater.h in Headers */,
61B5FC0D09C4FC8200B25A18 /* SUAppcast.h in Headers */,
- 61B5FC0E09C4FC8400B25A18 /* SUUtilities.h in Headers */,
61B5FC7009C51F4A00B25A18 /* SUAppcastItem.h in Headers */,
61B5FCDF09C52A9F00B25A18 /* SUUpdateAlert.h in Headers */,
6196CFF909C72148000DC222 /* SUStatusController.h in Headers */,
@@ -465,8 +473,11 @@
61299B3609CB04E000B7442F /* Sparkle.h in Headers */,
6120721209CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h in Headers */,
345AF9E40A5D707200D7DA6F /* SUStatusChecker.h in Headers */,
- D18A64E80CE2588900C49C71 /* SUBundleDefaults.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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -650,7 +661,6 @@
61B5F8EE09C4CE3C00B25A18 /* SUUpdater.m in Sources */,
61B5F8EF09C4CE3C00B25A18 /* NSFileManager+Authentication.m in Sources */,
61B5FBB709C4FAFF00B25A18 /* SUAppcast.m in Sources */,
- 61B5FC0F09C4FC8500B25A18 /* SUUtilities.m in Sources */,
61B5FC6F09C51F4900B25A18 /* SUAppcastItem.m in Sources */,
61B5FCDE09C52A9F00B25A18 /* SUUpdateAlert.m in Sources */,
6196CFFA09C72149000DC222 /* SUStatusController.m in Sources */,
@@ -660,9 +670,11 @@
61299A8E09CA790200B7442F /* SUUnarchiver.m in Sources */,
6120721309CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m in Sources */,
345AF9E50A5D707200D7DA6F /* SUStatusChecker.m in Sources */,
- D18A64E90CE2588900C49C71 /* SUBundleDefaults.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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
2  Sparkle_Prefix.pch
@@ -2,6 +2,8 @@
// Prefix header for all source files of the 'Sparkle' target in the 'Sparkle' project.
//
+#define SULocalizedString(key,comment) NSLocalizedStringFromTableInBundle(key, @"Sparkle", [NSBundle bundleForClass:[self class]], comment)
+
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#import "SUConstants.h"
Please sign in to comment.
Something went wrong with that request. Please try again.