5 // Created by Andy Matuschak on 8/10/07.
6 // Copyright 2007 __MyCompanyName__. All rights reserved.
10 #import "NDAlias/NDAlias.h"
12 @interface SUProduct (Private)
13 - (NSImage *)genericApplicationIcon;
14 - (NSArray *)codedKeys;
17 @implementation SUProduct
19 - (SUProduct *)initWithMainBundle:(NSBundle *)bundle
22 // Create an alias to the icon URL.
23 NSString *iconPath = [bundle pathForImageResource:@"NSApplicationIcon"];
25 [self setIconPath:[bundle pathForImageResource:@"NSApplicationIcon"]];
27 // Find the product name from the bundle.
28 NSString *productName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
30 productName = [[[NSFileManager defaultManager] displayNameAtPath:[bundle bundlePath]] stringByDeletingPathExtension]; // Make one up from the path.
31 [self setValue:productName forKey:@"name"];
33 // Find the Sparkle feed URL.
34 NSString *feed = [bundle objectForInfoDictionaryKey:@"SUFeedURL"];
36 [NSException raise:@"SUNoFeedException" format:@"You must include an SUFeedURL key in Info.plist for Sparkle to know where to look!"];
37 [self setValue:[NSURL URLWithString:feed] forKey:@"feedURL"];
39 // Now we set the bundle as the first path in the list of paths.
40 [self insertObject:[bundle bundlePath] inPathsAtIndex:0];
41 mainBundleAlias = [pathAliases objectAtIndex:0]; // Keep a pointer to the main bundle so we can get the version from it.
45 - (void)encodeWithCoder:(NSCoder *)coder
47 if ([coder allowsKeyedCoding])
49 NSEnumerator *keyEnumerator = [[self codedKeys] objectEnumerator];
51 while (key = [keyEnumerator nextObject])
53 [coder encodeObject:[self valueForKey:key] forKey:key];
55 [coder encodeConditionalObject:mainBundleAlias forKey:@"mainBundleAlias"]; // Encode this separately, since it's just a reference.
59 // NSPortCoder doesn't support keyed coding, so we just send over the archived data.
60 [coder encodeDataObject:[NSKeyedArchiver archivedDataWithRootObject:self]];
64 - initWithCoder:(NSCoder *)coder
66 // NSPortCoder doesn't support keyed coding, so we just send over the archived data.
67 if ([coder allowsKeyedCoding])
70 NSEnumerator *keyEnumerator = [[self codedKeys] objectEnumerator];
72 while (key = [keyEnumerator nextObject])
74 [self setValue:[coder decodeObjectForKey:key] forKey:key];
76 mainBundleAlias = [coder decodeObjectForKey:@"mainBundleAlias"]; // Decode this separately, since it's just a reference.
81 // NSPortCoder doesn't support keyed coding, so we just take archived data and make a new product from that.
82 return [NSKeyedUnarchiver unarchiveObjectWithData:[coder decodeDataObject]];
86 - copyWithZone:(NSZone *)zone
88 return [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
91 // Keys that should be archived for NSCoding.
92 - (NSArray *)codedKeys
94 return [NSArray arrayWithObjects:@"name", @"iconAlias", @"feedURL", @"pathAliases", nil];
99 if (!icon) // Lazily cache the icon.
101 if ([self valueForKey:@"iconAlias"])
102 icon = [[NSImage alloc] initWithContentsOfFile:[[self valueForKey:@"iconAlias"] path]];
104 return [self genericApplicationIcon]; // In case the app doesn't have an icon.
109 // Abstract away the NDAlias for the icon path.
110 - (NSString *)iconPath
112 return [[self valueForKey:@"iconAlias"] path];
115 - (void)setIconPath:(NSString *)path
118 icon = nil; // We'll want to reload it next time it's asked for.
119 [self setValue:[NDAlias aliasWithPath:path] forKey:@"iconAlias"];
122 // Put a nice KVC-compliant wrapper around the paths to get around the NDAlias messiness.
125 return [pathAliases valueForKey:@"path"];
128 - (void)insertObject:(NSString *)path inPathsAtIndex:(int)index
130 if (pathAliases == nil) { pathAliases = [[NSMutableArray array] retain]; }
131 if (![[pathAliases valueForKey:@"path"] containsObject:path])
132 [pathAliases insertObject:[NDAlias aliasWithPath:path] atIndex:index];
135 - (void)removeObjectFromPathsAtIndex:(int)index
137 [pathAliases removeObjectAtIndex:index];
140 - (void)removeAllObjectsFromPaths
142 [pathAliases removeAllObjects];
151 [pathAliases release];
155 - (NSImage *)genericApplicationIcon
157 return [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
160 // We implement this to let the coder know that we actually want to send the *contents* over the line when it's passed bycopy.
161 - replacementObjectForPortCoder:(NSPortCoder *)encoder
163 if ([encoder isByref])
164 return [NSDistantObject proxyWithLocal:self connection:[encoder connection]];