5 // Created by Chaz McGarvey on 1/5/05.
6 // Copyright 2005 Chaz McGarvey. All rights reserved.
12 // an NSError that an have an error statement associated with it
13 @interface ChazUpdateError
: NSError
15 NSString
*_errorString
;
17 + (id)errorWithString
:(NSString
*)string
;
18 - (id)initWithString
:(NSString
*)string
;
19 - (void)setErrorString
:(NSString
*)string
;
20 - (NSString
*)localizedDescription
;
22 @implementation ChazUpdateError
23 + (id)errorWithString
:(NSString
*)string
25 return [[[ChazUpdateError alloc
] initWithString
:string
] autorelease
];
27 - (id)initWithString
:(NSString
*)string
29 if ( self = [super initWithDomain
:@
"ChazUpdateError" code
:1 userInfo
:nil] ) {
30 [self setErrorString
:string
];
34 - (void)setErrorString
:(NSString
*)string
37 [_errorString release
];
38 _errorString
= string
;
40 - (NSString
*)localizedDescription
47 @interface ChazUpdate
: NSObject
51 NSURLConnection
*_connection
;
52 NSHTTPURLResponse
*_response
;
53 NSMutableData
*_responseData
;
58 - (id)initWithURL
:(id)url name
:(NSString
*)name verbose
:(BOOL)verbose
;
64 @implementation ChazUpdate
67 #pragma mark Initialization
69 - (id)initWithURL
:(id)url name
:(NSString
*)name verbose
:(BOOL)verbose
73 if ( self = [super init
] )
75 // figure out what type of URL was passed
76 if ( [url isKindOfClass
:[NSURL
class]] ) {
79 else if ( [url isKindOfClass
:[NSString
class]] ) {
80 theURL
= [[[NSURL alloc
] initWithString
:url
] autorelease
];
84 NSRunAlertPanel( @
"Update Check Failed",
85 @
"Could not determine the location of the version information file.", @
"OK", nil, nil );
92 _name
= [name retain
];
95 _name
= [[NSString stringWithString
:@
""] retain
];
100 _connection
= [[NSURLConnection connectionWithRequest
:[NSURLRequest requestWithURL
:theURL
101 cachePolicy
:NSURLRequestReloadIgnoringCacheData
102 timeoutInterval
:30.0] delegate
:self] retain
];
104 _responseData
= [[NSMutableData alloc
] init
];
115 [_connection release
];
117 [_responseData release
];
129 #pragma mark NSURLConnection Delegate
131 - (void)connection
:(NSURLConnection
*)connection didReceiveResponse
:(NSURLResponse
*)response
134 // another response, chop the data
135 [_responseData setLength
:0];
138 _response
= [response retain
];
141 - (void)connection
:(NSURLConnection
*)connection didReceiveData
:(NSData
*)data
143 // just save the data
144 [_responseData appendData
:data
];
147 -(void)connectionDidFinishLoading
:(NSURLConnection
*)connection
149 NSDictionary
*plistDictionary
;
150 NSPropertyListFormat plistFormat
;
151 NSString
*plistError
;
153 NSDictionary
*appDictionary
;
155 NSString
*appRedirect
;
156 NSString
*appVersion
;
158 NSDate
*appReleaseDate
;
159 NSString
*appDownload
;
160 NSString
*appDescription
;
162 // read in the recently downloaded plist
163 plistDictionary
= [NSPropertyListSerialization propertyListFromData
:_responseData
164 mutabilityOption
:NSPropertyListImmutable
166 errorDescription
:&plistError
];
167 if ( !plistDictionary
) {
168 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:plistError
]];
171 // make sure the root object is a dictionary
172 if ( ![NSStringFromClass([plistDictionary
class]) isEqualToString
:@
"NSCFDictionary"] ) {
173 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"the version information file is ill-formatted"]];
176 appDictionary
= [plistDictionary objectForKey
:_name
];
177 if ( !appDictionary
) {
178 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"no version information for this application"]];
182 // see if the version information for this app is in another file
183 if ( appRedirect
= [appDictionary objectForKey
:@
"Redirect"] ) {
184 // recursively follow the redirection
185 [[ChazUpdate alloc
] initWithURL
:appRedirect name
:_name verbose
:_verbose
];
190 // read other useful information about the app
191 appVersion
= [appDictionary objectForKey
:@
"Version"];
192 appReleaseDate
= [NSDate dateWithNaturalLanguageString
:[appDictionary objectForKey
:@
"ReleaseDate"]];
193 appOnline
= [appDictionary objectForKey
:@
"MoreInfoURL"];
194 appDownload
= [appDictionary objectForKey
:@
"DownloadURL"];
195 appDescription
= [appDictionary objectForKey
:@
"Description"];
197 if ( !appReleaseDate
) {
198 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"missing version information"]];
206 if ( [ChazAppBuildDate() compare
:appReleaseDate
] == NSOrderedAscending
) {
207 // there is a new version, display message
215 if ( appDescription
) {
216 desc
= appDescription
;
219 desc
= [NSString stringWithFormat
:@
"A new version of %@ is available! Would you like to learn more about the new version now?", _name
];
222 mainBtn
= @
"Learn More";
230 alternate
= @
"Download";
236 choice
= NSRunInformationalAlertPanel( [NSString stringWithFormat
:@
"New Version: %@ %@", _name
, appVersion
],
237 desc
, mainBtn
, alternate
, other
);
239 if ( choice
== NSAlertDefaultReturn
&& appOnline
) {
240 [[NSWorkspace sharedWorkspace
] openURL
:[NSURL URLWithString
:appOnline
]];
242 else if ( choice
== NSAlertAlternateReturn
) {
243 [[NSWorkspace sharedWorkspace
] openURL
:[NSURL URLWithString
:appDownload
]];
246 else if ( _verbose
) {
247 // alert that the user has the latest version if we're in verbose mode
248 NSRunInformationalAlertPanel( [NSString stringWithFormat
:@
"%@ %@", _name
, ChazAppVersion()],
249 @
"You have the latest version of %@.", @
"OK", nil, nil, _name
);
255 - (void)connection
:(NSURLConnection
*)connection didFailWithError
:(NSError
*)error
259 // check failed, display an error
260 NSRunAlertPanel( @
"Update Check Failed", @
"Check could not get new version data.\nError code #%i (%@)", @
"OK", nil, nil, [error code
], [error localizedDescription
] );
266 - (NSURLRequest
*)connection
:(NSURLConnection
*)connection willSendRequest
:(NSURLRequest
*)request redirectResponse
:(NSURLResponse
*)redirectResponse
268 // handle http redirection
276 #pragma mark API Functions
278 void ChazCheckForUpdate( id url
, BOOL userRequested
)
280 [[ChazUpdate alloc
] initWithURL
:url name
:ChazAppName() verbose
:userRequested
];
283 void ChazCheckForUpdateWithName( id url
, NSString
*appName
, BOOL userRequested
)
285 [[ChazUpdate alloc
] initWithURL
:url name
:appName verbose
:userRequested
];
289 NSString
*ChazAppName()
293 name
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleExecutable"];
294 if ( name
&& ![name isEqualToString
:@
""] ) {
297 name
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleName"];
298 if ( name
&& ![name isEqualToString
:@
""] ) {
304 NSString
*ChazAppVersion()
308 vers
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleShortVersionString"];
309 if ( vers
&& ![vers isEqualToString
:@
""] ) {
312 vers
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleVersion"];
313 if ( vers
&& ![vers isEqualToString
:@
""] ) {
319 NSDate
*ChazAppBuildDate()
321 return [NSDate dateWithNaturalLanguageString
:[NSString stringWithFormat
:@
"%s %s", __TIME__
, __DATE__
]];