Ben Copsey

Merge branch 'downloadcache' into v1.7

Conflicts:
	Classes/ASIHTTPRequest.m
	Classes/Tests/ASIHTTPRequestTests.m
	Mac.xcodeproj/project.pbxproj
	iPhone.xcodeproj/project.pbxproj
//
// ASICacheDelegate.h
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 01/05/2010.
// Copyright 2010 All-Seeing Interactive. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ASIHTTPRequest;
typedef enum _ASICachePolicy {
ASIDefaultCachePolicy = 0,
ASIIgnoreCachePolicy = 1,
ASIReloadIfDifferentCachePolicy = 2,
ASIOnlyLoadIfNotCachedCachePolicy = 3,
ASIUseCacheIfLoadFailsCachePolicy = 4
} ASICachePolicy;
typedef enum _ASICacheStoragePolicy {
ASICacheForSessionDurationCacheStoragePolicy = 0,
ASICachePermanentlyCacheStoragePolicy = 1
} ASICacheStoragePolicy;
@protocol ASICacheDelegate <NSObject>
@required
// Should return the cache policy that will be used when requests have their cache policy set to ASIDefaultCachePolicy
- (ASICachePolicy)defaultCachePolicy;
// Should Remove cached data for a particular request
- (void)removeCachedDataForRequest:(ASIHTTPRequest *)request;
// Should return YES if the cache considers its cached response current for the request
// Should return NO is the data is not cached, or (for example) if the cached headers state the request should have expired
- (BOOL)isCachedDataCurrentForRequest:(ASIHTTPRequest *)request;
// Should store the response for the passed request in the cache
// When a non-zero maxAge is passed, it should be used as the expiry time for the cached response
- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
// Should return an NSDictionary of cached headers for the passed request, if it is stored in the cache
- (NSDictionary *)cachedHeadersForRequest:(ASIHTTPRequest *)request;
// Should return the cached body of a response for the passed request, if it is stored in the cache
- (NSData *)cachedResponseDataForRequest:(ASIHTTPRequest *)request;
// Same as the above, but returns a path to the cached response body instead
- (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request;
// Clear cached data stored for the passed storage policy
- (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)cachePolicy;
@end
... ...
//
// ASIDownloadCache.h
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 01/05/2010.
// Copyright 2010 All-Seeing Interactive. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "ASICacheDelegate.h"
@interface ASIDownloadCache : NSObject <ASICacheDelegate> {
// The default cache policy for this cache
// Requests that store data in the cache will use this cache policy if their cache policy is set to ASIDefaultCachePolicy
// Defaults to ASIReloadIfDifferentCachePolicy
ASICachePolicy defaultCachePolicy;
// The directory in which cached data will be stored
// Defaults to a directory called 'ASIHTTPRequestCache' in the temporary directory
NSString *storagePath;
// Mediates access to the cache
NSRecursiveLock *accessLock;
// When YES, the cache will look for cache-control / pragma: no-cache headers, and won't reuse store responses if it finds them
BOOL shouldRespectCacheControlHeaders;
}
// Returns a static instance of an ASIDownloadCache
// In most circumstances, it will make sense to use this as a global cache, rather than creating your own cache
// To make ASIHTTPRequests use it automatically, use [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];
+ (id)sharedCache;
// A helper function that determines if the server has requested data should not be cached by looking at the request's response headers
+ (BOOL)serverAllowsResponseCachingForRequest:(ASIHTTPRequest *)request;
@property (assign) ASICachePolicy defaultCachePolicy;
@property (retain) NSString *storagePath;
@property (retain) NSRecursiveLock *accessLock;
@property (assign) BOOL shouldRespectCacheControlHeaders;
@end
... ...
This diff is collapsed. Click to expand it.
... ... @@ -18,6 +18,7 @@
#import "ASIHTTPRequestConfig.h"
#import "ASIHTTPRequestDelegate.h"
#import "ASIProgressDelegate.h"
#import "ASICacheDelegate.h"
extern NSString *ASIHTTPRequestVersion;
... ... @@ -398,6 +399,22 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Will be ASIHTTPRequestRunLoopMode for synchronous requests, NSDefaultRunLoopMode for all other requests
NSString *runLoopMode;
// The download cache that will be used for this request (use [ASIHTTPRequest setDefaultCache:cache] to configure a default cache
id <ASICacheDelegate> downloadCache;
// The cache policy that will be used for this request - See ASICacheDelegate.h for possible values
ASICachePolicy cachePolicy;
// The cache storage policy that will be used for this request - See ASICacheDelegate.h for possible values
ASICacheStoragePolicy cacheStoragePolicy;
// Will be true when the response was pulled from the cache rather than downloaded
BOOL didUseCachedResponse;
// Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache
NSTimeInterval secondsToCache;
}
#pragma mark init / dealloc
... ... @@ -408,6 +425,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Convenience constructor
+ (id)requestWithURL:(NSURL *)newURL;
+ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache;
+ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache andCachePolicy:(ASICachePolicy)policy;
#pragma mark setup request
// Add a custom header to the request
... ... @@ -677,6 +697,11 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
#endif
#pragma mark cache
+ (void)setDefaultCache:(id <ASICacheDelegate>)cache;
+ (id <ASICacheDelegate>)defaultCache;
// Returns the maximum amount of data we can read as part of the current measurement period, and sleeps this thread if our allowance is used up
+ (unsigned long)maxUploadReadLength;
... ... @@ -689,6 +714,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// And also by ASIS3Request
+ (NSString *)base64forData:(NSData *)theData;
// Returns a date from a string in RFC1123 format
+ (NSDate *)dateFromRFC1123String:(NSString *)string;
#pragma mark ===
@property (retain) NSString *username;
... ... @@ -721,7 +749,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
@property (retain,readonly) NSString *proxyAuthenticationRealm;
@property (retain) NSError *error;
@property (assign,readonly) BOOL complete;
@property (retain,readonly) NSDictionary *responseHeaders;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSMutableDictionary *requestHeaders;
@property (retain) NSMutableArray *requestCookies;
@property (retain,readonly) NSArray *responseCookies;
... ... @@ -730,7 +758,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
@property (retain) NSDictionary *proxyCredentials;
@property (assign,readonly) int responseStatusCode;
@property (retain,readonly) NSString *responseStatusMessage;
@property (retain,readonly) NSMutableData *rawResponseData;
@property (retain) NSMutableData *rawResponseData;
@property (assign) NSTimeInterval timeOutSeconds;
@property (retain) NSString *requestMethod;
@property (retain) NSMutableData *postBody;
... ... @@ -775,4 +803,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
@property (assign) BOOL shouldUseRFC2616RedirectBehaviour;
@property (assign, readonly) BOOL connectionCanBeReused;
@property (retain, readonly) NSNumber *requestID;
@property (assign) id <ASICacheDelegate> downloadCache;
@property (assign) ASICachePolicy cachePolicy;
@property (assign) ASICacheStoragePolicy cacheStoragePolicy;
@property (assign, readonly) BOOL didUseCachedResponse;
@property (assign) NSTimeInterval secondsToCache;
@end
... ...
... ... @@ -23,6 +23,7 @@
// Automatically set on build
NSString *ASIHTTPRequestVersion = @"v1.6.2-37 2010-06-23";
NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
... ... @@ -121,6 +122,8 @@ static NSDate *throttleWakeUpTime = nil;
// Run once in initalize to record at runtime whether we're on iPhone OS 2. When YES, a workaround for a type conversion bug in iPhone OS 2.2.x is applied in some places
static BOOL isiPhoneOS2;
static id <ASICacheDelegate> defaultCache = nil;
// Private stuff
@interface ASIHTTPRequest ()
... ... @@ -143,6 +146,8 @@ static BOOL isiPhoneOS2;
- (void)updateStatus:(NSTimer*)timer;
- (BOOL)useDataFromCache;
#if TARGET_OS_IPHONE
+ (void)registerForNetworkReachabilityNotifications;
+ (void)unsubscribeFromNetworkReachabilityNotifications;
... ... @@ -151,10 +156,8 @@ static BOOL isiPhoneOS2;
#endif
@property (assign) BOOL complete;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSArray *responseCookies;
@property (assign) int responseStatusCode;
@property (retain) NSMutableData *rawResponseData;
@property (retain, nonatomic) NSDate *lastActivityTime;
@property (assign) unsigned long long contentLength;
@property (assign) unsigned long long partialDownloadSize;
... ... @@ -190,6 +193,7 @@ static BOOL isiPhoneOS2;
@property (assign, nonatomic) BOOL downloadComplete;
@property (retain) NSNumber *requestID;
@property (assign, nonatomic) NSString *runLoopMode;
@property (assign) BOOL didUseCachedResponse;
@end
... ... @@ -252,6 +256,7 @@ static BOOL isiPhoneOS2;
[self setDidReceiveDataSelector:@selector(request:didReceiveData:)];
[self setURL:newURL];
[self setCancelledLock:[[[NSRecursiveLock alloc] init] autorelease]];
[self setDownloadCache:[[self class] defaultCache]];
return self;
}
... ... @@ -260,6 +265,19 @@ static BOOL isiPhoneOS2;
return [[[self alloc] initWithURL:newURL] autorelease];
}
+ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache
{
return [self requestWithURL:newURL usingCache:cache andCachePolicy:ASIDefaultCachePolicy];
}
+ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache andCachePolicy:(ASICachePolicy)policy
{
ASIHTTPRequest *request = [[[self alloc] initWithURL:newURL] autorelease];
[request setDownloadCache:cache];
[request setCachePolicy:policy];
return request;
}
- (void)dealloc
{
[statusTimer invalidate];
... ... @@ -607,6 +625,10 @@ static BOOL isiPhoneOS2;
[self buildPostBody];
}
if (![[self requestMethod] isEqualToString:@"GET"]) {
[self setDownloadCache:nil];
}
// If we're redirecting, we'll already have a CFHTTPMessageRef
if (request) {
CFRelease(request);
... ... @@ -627,6 +649,33 @@ static BOOL isiPhoneOS2;
// Even if this is a HEAD request with a mainRequest, we still need to call to give subclasses a chance to add their own to HEAD requests (ASIS3Request does this)
[self buildRequestHeaders];
if ([self downloadCache]) {
if ([self cachePolicy] == ASIDefaultCachePolicy) {
[self setCachePolicy:[[self downloadCache] defaultCachePolicy]];
}
// See if we should pull from the cache rather than fetching the data
if ([self cachePolicy] == ASIOnlyLoadIfNotCachedCachePolicy) {
if ([self useDataFromCache]) {
return;
}
} else if ([self cachePolicy] == ASIReloadIfDifferentCachePolicy) {
// Force a conditional GET if we have a cached version of this content already
NSDictionary *cachedHeaders = [[self downloadCache] cachedHeadersForRequest:self];
if (cachedHeaders) {
NSString *etag = [cachedHeaders objectForKey:@"Etag"];
if (etag) {
[[self requestHeaders] setObject:etag forKey:@"If-None-Match"];
}
NSString *lastModified = [cachedHeaders objectForKey:@"Last-Modified"];
if (lastModified) {
[[self requestHeaders] setObject:lastModified forKey:@"If-Modified-Since"];
}
}
}
}
[self applyAuthorizationHeader];
... ... @@ -768,7 +817,6 @@ static BOOL isiPhoneOS2;
}
[self addRequestHeader:@"Range" value:[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]];
}
}
- (void)startRequest
... ... @@ -1575,6 +1623,13 @@ static BOOL isiPhoneOS2;
return;
}
if ([self downloadCache] && [self cachePolicy] == ASIUseCacheIfLoadFailsCachePolicy) {
if ([self useDataFromCache]) {
return;
}
}
[self setError:theError];
ASIHTTPRequest *failedRequest = self;
... ... @@ -1628,8 +1683,16 @@ static BOOL isiPhoneOS2;
[self setResponseHeaders:(NSDictionary *)headerFields];
CFRelease(headerFields);
[self setResponseStatusCode:(int)CFHTTPMessageGetResponseStatusCode(message)];
[self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(message) autorelease]];
if ([self downloadCache] && [self cachePolicy] == ASIReloadIfDifferentCachePolicy) {
if ([self useDataFromCache]) {
CFRelease(message);
return;
}
}
// Is the server response a challenge for credentials?
if ([self responseStatusCode] == 401) {
... ... @@ -2452,6 +2515,11 @@ static BOOL isiPhoneOS2;
[self readResponseHeaders];
}
// If we've cancelled the load part way through (for example, after deciding to use a cached version)
if ([self complete]) {
return;
}
// In certain (presumably very rare) circumstances, handleBytesAvailable seems to be called when there isn't actually any data available
// We'll check that there is actually data available to prevent blocking on CFReadStreamRead()
// So far, I've only seen this in the stress tests, so it might never happen in real-world situations.
... ... @@ -2611,6 +2679,12 @@ static BOOL isiPhoneOS2;
}
}
// Save to the cache
if ([self downloadCache]) {
[[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]];
}
[progressLock unlock];
... ... @@ -2661,6 +2735,52 @@ static BOOL isiPhoneOS2;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (BOOL)useDataFromCache
{
NSDictionary *headers = [[self downloadCache] cachedHeadersForRequest:self];
if (!headers) {
return NO;
}
NSString *dataPath = [[self downloadCache] pathToCachedResponseDataForRequest:self];
if (!dataPath) {
return NO;
}
if ([self cachePolicy] == ASIReloadIfDifferentCachePolicy) {
if (![[self downloadCache] isCachedDataCurrentForRequest:self]) {
[[self downloadCache] removeCachedDataForRequest:self];
return NO;
}
}
[self setDidUseCachedResponse:YES];
ASIHTTPRequest *theRequest = self;
if ([self mainRequest]) {
theRequest = [self mainRequest];
}
[theRequest setResponseHeaders:headers];
if ([theRequest downloadDestinationPath]) {
[theRequest setDownloadDestinationPath:dataPath];
} else {
[theRequest setRawResponseData:[NSMutableData dataWithContentsOfFile:dataPath]];
}
[theRequest setContentLength:[[[self responseHeaders] objectForKey:@"Content-Length"] longLongValue]];
[theRequest setTotalBytesRead:[self contentLength]];
[theRequest setComplete:YES];
[theRequest setDownloadComplete:YES];
[theRequest updateProgressIndicators];
[theRequest requestFinished];
[theRequest markAsFinished];
if ([self mainRequest]) {
[self markAsFinished];
}
return YES;
}
- (BOOL)retryUsingNewConnection
{
if ([self retryCount] == 0) {
... ... @@ -3055,6 +3175,7 @@ static BOOL isiPhoneOS2;
[[[self class] sessionCredentialsStore] removeAllObjects];
[sessionCredentialsLock unlock];
[[self class] setSessionCookies:nil];
[[[self class] defaultCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
}
#pragma mark gzip decompression
... ... @@ -3659,6 +3780,18 @@ static BOOL isiPhoneOS2;
#endif
+ (void)setDefaultCache:(id <ASICacheDelegate>)cache
{
[defaultCache release];
defaultCache = [cache retain];
}
+ (id <ASICacheDelegate>)defaultCache
{
return defaultCache;
}
#pragma mark miscellany
+ (BOOL)isiPhoneOS2
... ... @@ -3701,6 +3834,25 @@ static BOOL isiPhoneOS2;
return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
// Based on hints from http://stackoverflow.com/questions/1850824/parsing-a-rfc-822-date-with-nsdateformatter
+ (NSDate *)dateFromRFC1123String:(NSString *)string
{
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
// Does the string include a week day?
NSString *day = @"";
if ([string rangeOfString:@","].location != NSNotFound) {
day = @"EEE, ";
}
// Does the string include seconds?
NSString *seconds = @"";
if ([[string componentsSeparatedByString:@":"] count] == 3) {
seconds = @":ss";
}
[formatter setDateFormat:[NSString stringWithFormat:@"%@dd MMM yyyy HH:mm%@ z",day,seconds]];
return [formatter dateFromString:string];
}
#pragma mark ===
@synthesize username;
... ... @@ -3803,4 +3955,9 @@ static BOOL isiPhoneOS2;
@synthesize downloadComplete;
@synthesize requestID;
@synthesize runLoopMode;
@synthesize downloadCache;
@synthesize cachePolicy;
@synthesize cacheStoragePolicy;
@synthesize didUseCachedResponse;
@synthesize secondsToCache;
@end
... ...
//
// ASIDownloadCacheTests.h
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 03/05/2010.
// Copyright 2010 All-Seeing Interactive. All rights reserved.
//
#import "ASITestCase.h"
@interface ASIDownloadCacheTests : ASITestCase {
}
@end
... ...
This diff is collapsed. Click to expand it.
... ... @@ -1586,6 +1586,7 @@
[[self responseData] appendData:data];
}
- (void)testNilPortCredentialsMatching
{
// Test for http://github.com/pokeb/asi-http-request/issues#issue/39
... ... @@ -1612,5 +1613,26 @@
- (void)testRFC1123DateParsing
{
unsigned dateUnits = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit;
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
NSString *dateString = @"Thu, 19 Nov 1981 08:52:01 GMT";
NSDate *date = [ASIHTTPRequest dateFromRFC1123String:dateString];
NSDateComponents *components = [calendar components:dateUnits fromDate:date];
NSLog(@"%i",[components weekday]);
BOOL success = ([components year] == 1981 && [components month] == 11 && [components day] == 19 && [components weekday] == 5 && [components hour] == 8 && [components minute] == 52 && [components second] == 1);
GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly");
dateString = @"4 May 2010 00:59 CET";
date = [ASIHTTPRequest dateFromRFC1123String:dateString];
components = [calendar components:dateUnits fromDate:date];
success = ([components year] == 2010 && [components month] == 5 && [components day] == 3 && [components hour] == 23 && [components minute] == 59);
GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly");
}
@synthesize responseData;
@end
\ No newline at end of file
... ...
... ... @@ -40,6 +40,11 @@
ASIHTTPRequest *bigFetchRequest;
IBOutlet NSTextField *postStatus;
IBOutlet NSTableView *tableView;
IBOutlet NSTextField *tableLoadStatus;
NSMutableArray *rowData;
ASINetworkQueue *tableQueue;
}
- (IBAction)simpleURLFetch:(id)sender;
... ... @@ -57,6 +62,10 @@
- (IBAction)throttleBandwidth:(id)sender;
- (IBAction)reloadTableData:(id)sender;
- (IBAction)clearCache:(id)sender;
@property (retain, nonatomic) ASIHTTPRequest *bigFetchRequest;
@property (retain, nonatomic) NSMutableArray *rowData;
@property (retain, nonatomic) ASINetworkQueue *tableQueue;
@end
... ...
... ... @@ -9,6 +9,7 @@
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"
#import "ASINetworkQueue.h"
#import "ASIDownloadCache.h"
@interface AppDelegate ()
- (void)updateBandwidthUsageIndicator;
... ... @@ -25,6 +26,7 @@
@implementation AppDelegate
- (id)init
{
[super init];
... ... @@ -308,5 +310,77 @@
}
- (IBAction)reloadTableData:(id)sender
{
[[self tableQueue] cancelAllOperations];
[self setRowData:[NSMutableArray array]];
[tableView reloadData];
[self setTableQueue:[ASINetworkQueue queue]];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/table-row-data.xml"]];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request setDidFinishSelector:@selector(tableViewDataFetchFinished:)];
[request setDelegate:self];
[[self tableQueue] addOperation:request];
[[self tableQueue] go];
}
- (void)tableViewDataFetchFailed:(ASIHTTPRequest *)request
{
if ([[request error] domain] != NetworkRequestErrorDomain || ![[request error] code] == ASIRequestCancelledErrorType) {
[tableLoadStatus setStringValue:@"Loading data failed"];
}
}
- (void)tableViewDataFetchFinished:(ASIHTTPRequest *)request
{
NSXMLDocument *xml = [[[NSXMLDocument alloc] initWithData:[request responseData] options:NSXMLDocumentValidate error:nil] autorelease];
for (NSXMLElement *row in [[xml rootElement] elementsForName:@"row"]) {
NSMutableDictionary *rowInfo = [NSMutableDictionary dictionary];
NSString *description = [[[row elementsForName:@"description"] objectAtIndex:0] stringValue];
[rowInfo setValue:description forKey:@"description"];
NSString *imageURL = [[[row elementsForName:@"image"] objectAtIndex:0] stringValue];
ASIHTTPRequest *imageRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:imageURL]];
[imageRequest setDownloadCache:[ASIDownloadCache sharedCache]];
[imageRequest setDidFinishSelector:@selector(rowImageDownloadFinished:)];
[imageRequest setDidFailSelector:@selector(tableViewDataFetchFailed:)];
[imageRequest setDelegate:self];
[imageRequest setUserInfo:rowInfo];
[[self tableQueue] addOperation:imageRequest];
[[self rowData] addObject:rowInfo];
}
[tableView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [[self rowData] count];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
if ([[aTableColumn identifier] isEqualToString:@"image"]) {
return [[[self rowData] objectAtIndex:rowIndex] objectForKey:@"image"];
} else {
return [[[self rowData] objectAtIndex:rowIndex] objectForKey:@"description"];
}
}
- (void)rowImageDownloadFinished:(ASIHTTPRequest *)request
{
NSImage *image = [[[NSImage alloc] initWithData:[request responseData]] autorelease];
[(NSMutableDictionary *)[request userInfo] setObject:image forKey:@"image"];
[tableView reloadData]; // Not efficient, but I hate table view programming :)
}
- (IBAction)clearCache:(id)sender
{
[[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
}
@synthesize bigFetchRequest;
@synthesize rowData;
@synthesize tableQueue;
@end
... ...
This diff is collapsed. Click to expand it.