Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Functionality of T3URLCache expanded and stabilized greatly

  • Loading branch information...
commit 5a05691e1d9b74771411b76cfcd9927822db27ec 1 parent 5e002c4
@joehewitt joehewitt authored
View
8 src/T3ActivityLabel.m
@@ -15,13 +15,13 @@ @implementation T3ActivityLabel
@synthesize style = _style, centered = _centered, centeredToScreen = _centeredToScreen;
-- (id)initWithFrame:(CGRect)frame style:(T3ActivityLabelStyle)aStyle {
- return [self initWithFrame:frame style:aStyle text:nil];
+- (id)initWithFrame:(CGRect)frame style:(T3ActivityLabelStyle)style {
+ return [self initWithFrame:frame style:style text:nil];
}
-- (id)initWithFrame:(CGRect)frame style:(T3ActivityLabelStyle)aStyle text:(NSString*)text {
+- (id)initWithFrame:(CGRect)frame style:(T3ActivityLabelStyle)style text:(NSString*)text {
if (self = [super initWithFrame:frame]) {
- _style = aStyle;
+ _style = style;
_centered = YES;
_centeredToScreen = YES;
View
6 src/T3Global.m
@@ -463,8 +463,7 @@ - (UIScrollView*)findFirstScrollView {
if ([self isKindOfClass:[UIScrollView class]])
return (UIScrollView*)self;
- NSEnumerator* e = [self.subviews objectEnumerator];
- for (UIView* child; child = [e nextObject]; ) {
+ for (UIView* child in self.subviews) {
UIScrollView* it = [child findFirstScrollView];
if (it)
return it;
@@ -477,8 +476,7 @@ - (UIView*)firstViewOfClass:(Class)cls {
if ([self isKindOfClass:cls])
return self;
- NSEnumerator* e = [self.subviews objectEnumerator];
- for (UIView* child; child = [e nextObject]; ) {
+ for (UIView* child in self.subviews) {
UIView* it = [child firstViewOfClass:cls];
if (it)
return it;
View
2  src/T3ImageView.m
@@ -124,7 +124,7 @@ - (void)reload {
return;
_request = [[T3URLRequest alloc] initWithURL:_url delegate:self];
- _request.convertMedia = YES;
+ _request.shouldConvertToMedia = YES;
if (_url && ![_request send]) {
// Put the default image in place while waiting for the request to load
View
4 src/T3LinkView.m
@@ -81,9 +81,9 @@ - (void)setHighlighted:(BOOL)highlighted {
///////////////////////////////////////////////////////////////////////////////////////////////////
-- (void)setHref:(id)aHref {
+- (void)setHref:(id)href {
[_href release];
- _href = [aHref retain];
+ _href = [href retain];
self.userInteractionEnabled = !!_href;
}
View
18 src/T3NavigationCenter.m
@@ -11,8 +11,8 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
@interface T3NavigationEntry : NSObject {
- Class cls;
- T3NavigationRule rule;
+ Class _cls;
+ T3NavigationRule _rule;
}
@property(nonatomic,readonly) Class cls;
@@ -24,12 +24,12 @@ - (id)initWithClass:(Class)cls rule:(T3NavigationRule)rule;
@implementation T3NavigationEntry
-@synthesize cls, rule;
+@synthesize cls = _cls, rule = _rule;
-- (id)initWithClass:(Class)aClass rule:(T3NavigationRule)aRule {
+- (id)initWithClass:(Class)controllerClass rule:(T3NavigationRule)rule {
if (self = [super init]) {
- cls = aClass;
- rule = aRule;
+ _cls = controllerClass;
+ _rule = rule;
}
return self;
}
@@ -77,8 +77,7 @@ - (void)dealloc {
- (NSArray*)stateFromNavigationController:(UINavigationController*)navController {
NSMutableArray* states = [NSMutableArray array];
- NSEnumerator* e = [navController.viewControllers objectEnumerator];
- for (UIViewController* controller; controller = [e nextObject]; ) {
+ for (UIViewController* controller in navController.viewControllers) {
if (![self serializeController:controller states:states])
break;
}
@@ -336,8 +335,7 @@ - (T3ViewController*)displayURL:(NSString*)u withState:(NSDictionary*)state
}
if (!viewController.parentViewController) {
- NSEnumerator* e = [navController.viewControllers objectEnumerator];
- for (UIViewController* c; c = [e nextObject]; ) {
+ for (UIViewController* c in navController.viewControllers) {
if (c.hidesBottomBarWhenPushed) {
viewController.hidesBottomBarWhenPushed = NO;
break;
View
6 src/T3PhotoViewController.m
@@ -23,6 +23,7 @@ - (id)init {
_centerPhoto = nil;
_centerPhotoIndex = 0;
_previousBarStyle = 0;
+ _previousBarTintColor = nil;
_scrollView = nil;
_photoStatusView = nil;
_statusText = nil;
@@ -82,7 +83,7 @@ - (void)cancelImageLoadTimer {
- (void)loadImages {
T3PhotoView* centerPhotoView = self.centerPhotoView;
- for (T3PhotoView* photoView in _scrollView.visiblePages.objectEnumerator) {
+ for (T3PhotoView* photoView in [_scrollView.visiblePages objectEnumerator]) {
if (photoView == centerPhotoView) {
[photoView loadPreview:NO];
} else {
@@ -205,8 +206,10 @@ - (void)viewWillAppear:(BOOL)animated {
if (bar.barStyle != UIBarStyleBlackTranslucent) {
if (!self.nextViewController) {
_previousBarStyle = bar.barStyle;
+ _previousBarTintColor = bar.tintColor;
}
+ bar.tintColor = nil;
bar.barStyle = UIBarStyleBlackTranslucent;
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent
animated:YES];
@@ -230,6 +233,7 @@ - (void)viewWillDisappear:(BOOL)animated {
if (!self.nextViewController) {
UINavigationBar* bar = self.navigationController.navigationBar;
if (_previousBarStyle != UIBarStyleBlackTranslucent) {
+ bar.tintColor = _previousBarTintColor;
bar.barStyle = _previousBarStyle;
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
View
4 src/T3TableViewController.m
@@ -46,11 +46,11 @@ - (void)unloadView {
//////////////////////////////////////////////////////////////////////////////////////////////////
// UITableViewDataSource
-- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
-- (UITableViewCell *)tableView:(UITableView *)aTableView
+- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
View
4 src/T3ThumbView.m
@@ -74,8 +74,8 @@ - (NSString*)url {
return imageView.url;
}
-- (void)setUrl:(NSString*)aURL {
- imageView.url = aURL;
+- (void)setUrl:(NSString*)url {
+ imageView.url = url;
}
- (void)pauseLoading:(BOOL)paused {
View
110 src/T3ThumbsTableViewCell.m
@@ -11,35 +11,35 @@
@implementation T3ThumbsTableViewCell
-@synthesize photo;
+@synthesize photo = _photo;
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString*)identifier {
if (self = [super initWithFrame:frame reuseIdentifier:identifier]) {
- photo = nil;
+ _photo = nil;
- thumbView1 = [[T3ThumbView alloc]
+ _thumbView1 = [[T3ThumbView alloc]
initWithFrame:CGRectMake(kSpacing, 0, kThumbSize, kThumbSize)];
- [thumbView1 addTarget:self action:@selector(thumbTouched:)
+ [_thumbView1 addTarget:self action:@selector(thumbTouched:)
forControlEvents:UIControlEventTouchUpInside];
- [self.contentView addSubview:thumbView1];
+ [self.contentView addSubview:_thumbView1];
- thumbView2 = [[T3ThumbView alloc]
+ _thumbView2 = [[T3ThumbView alloc]
initWithFrame:CGRectMake(kSpacing*2+kThumbSize, 0, kThumbSize, kThumbSize)];
- [thumbView2 addTarget:self action:@selector(thumbTouched:)
+ [_thumbView2 addTarget:self action:@selector(thumbTouched:)
forControlEvents:UIControlEventTouchUpInside];
- [self.contentView addSubview:thumbView2];
+ [self.contentView addSubview:_thumbView2];
- thumbView3 = [[T3ThumbView alloc]
+ _thumbView3 = [[T3ThumbView alloc]
initWithFrame:CGRectMake(kSpacing*3+kThumbSize*2, 0, kThumbSize, kThumbSize)];
- [thumbView3 addTarget:self action:@selector(thumbTouched:)
+ [_thumbView3 addTarget:self action:@selector(thumbTouched:)
forControlEvents:UIControlEventTouchUpInside];
- [self.contentView addSubview:thumbView3];
+ [self.contentView addSubview:_thumbView3];
- thumbView4 = [[T3ThumbView alloc]
+ _thumbView4 = [[T3ThumbView alloc]
initWithFrame:CGRectMake(kSpacing*4+kThumbSize*3, 0, kThumbSize, kThumbSize)];
- [thumbView4 addTarget:self action:@selector(thumbTouched:)
+ [_thumbView4 addTarget:self action:@selector(thumbTouched:)
forControlEvents:UIControlEventTouchUpInside];
- [self.contentView addSubview:thumbView4];
+ [self.contentView addSubview:_thumbView4];
self.accessoryType = UITableViewCellAccessoryNone;
self.selectionStyle = UITableViewCellSelectionStyleNone;
@@ -48,74 +48,74 @@ - (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString*)identifier {
}
- (void)dealloc {
- [photo release];
- [thumbView1 release];
- [thumbView2 release];
- [thumbView3 release];
- [thumbView4 release];
+ [_photo release];
+ [_thumbView1 release];
+ [_thumbView2 release];
+ [_thumbView3 release];
+ [_thumbView4 release];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
-- (void)assignPhotoAtIndex:(int)index toView:(T3ThumbView*)thumbView {
- id<T3Photo> thePhoto = [photo.photoSource photoAtIndex:index];
- if (thePhoto) {
- thumbView.url = [thePhoto urlForVersion:T3PhotoVersionThumbnail];
- thumbView.hidden = NO;
+- (void)assignPhotoAtIndex:(int)index toView:(T3ThumbView*)_thumbView {
+ id<T3Photo> photo = [_photo.photoSource photoAtIndex:index];
+ if (photo) {
+ _thumbView.url = [photo urlForVersion:T3PhotoVersionThumbnail];
+ _thumbView.hidden = NO;
} else {
- thumbView.url = nil;
- thumbView.hidden = YES;
+ _thumbView.url = nil;
+ _thumbView.hidden = YES;
}
}
-- (void)thumbTouched:(T3ThumbView*)thumbView {
+- (void)thumbTouched:(T3ThumbView*)_thumbView {
NSUInteger index;
- if (thumbView == thumbView1) {
- index = photo.index;
- } else if (thumbView == thumbView2) {
- index = photo.index + 1;
- } else if (thumbView == thumbView3) {
- index = photo.index + 2;
- } else if (thumbView == thumbView4) {
- index = photo.index + 3;
+ if (_thumbView == _thumbView1) {
+ index = _photo.index;
+ } else if (_thumbView == _thumbView2) {
+ index = _photo.index + 1;
+ } else if (_thumbView == _thumbView3) {
+ index = _photo.index + 2;
+ } else if (_thumbView == _thumbView4) {
+ index = _photo.index + 3;
}
- id<T3Photo> thePhoto = [photo.photoSource photoAtIndex:index];
+ id<T3Photo> photo = [_photo.photoSource photoAtIndex:index];
UITableView* tableView = (UITableView*)self.superview;
if ([tableView.delegate respondsToSelector:@selector(tableView:didSelectPhoto:)]) {
[tableView.delegate performSelector:@selector(tableView:didSelectPhoto:)
- withObject:tableView withObject:thePhoto];
+ withObject:tableView withObject:photo];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
-- (void)setPhoto:(id<T3Photo>)aPhoto {
- if (photo != aPhoto) {
- [photo release];
- photo = [aPhoto retain];
- if (!photo) {
- thumbView1.url = nil;
- thumbView2.url = nil;
- thumbView3.url = nil;
- thumbView4.url = nil;
+- (void)setPhoto:(id<T3Photo>)photo {
+ if (_photo != photo) {
+ [_photo release];
+ _photo = [photo retain];
+ if (!_photo) {
+ _thumbView1.url = nil;
+ _thumbView2.url = nil;
+ _thumbView3.url = nil;
+ _thumbView4.url = nil;
return;
}
- thumbView1.url = [photo urlForVersion:T3PhotoVersionThumbnail];
- [self assignPhotoAtIndex:photo.index+1 toView:thumbView2];
- [self assignPhotoAtIndex:photo.index+1 toView:thumbView2];
- [self assignPhotoAtIndex:photo.index+2 toView:thumbView3];
- [self assignPhotoAtIndex:photo.index+3 toView:thumbView4];
+ _thumbView1.url = [_photo urlForVersion:T3PhotoVersionThumbnail];
+ [self assignPhotoAtIndex:_photo.index+1 toView:_thumbView2];
+ [self assignPhotoAtIndex:_photo.index+1 toView:_thumbView2];
+ [self assignPhotoAtIndex:_photo.index+2 toView:_thumbView3];
+ [self assignPhotoAtIndex:_photo.index+3 toView:_thumbView4];
}
}
- (void)pauseLoading:(BOOL)paused {
- [thumbView1 pauseLoading:paused];
- [thumbView2 pauseLoading:paused];
- [thumbView3 pauseLoading:paused];
- [thumbView4 pauseLoading:paused];
+ [_thumbView1 pauseLoading:paused];
+ [_thumbView2 pauseLoading:paused];
+ [_thumbView3 pauseLoading:paused];
+ [_thumbView4 pauseLoading:paused];
}
@end
View
14 src/T3ThumbsViewController.m
@@ -210,7 +210,7 @@ - (UITableViewCell *)tableView:(UITableView*)tableView
return cell;
}
-- (void)tableView:(UITableView*)aTableView didSelectPhoto:(id<T3Photo>)photo {
+- (void)tableView:(UITableView*)tableView didSelectPhoto:(id<T3Photo>)photo {
T3PhotoViewController* controller = [[[T3PhotoViewController alloc] init] autorelease];
controller.centerPhoto = photo;
[self.navigationController pushViewController:controller animated:YES];
@@ -219,12 +219,12 @@ - (void)tableView:(UITableView*)aTableView didSelectPhoto:(id<T3Photo>)photo {
///////////////////////////////////////////////////////////////////////////////////////////////////
// T3PhotoSourceDelegate
-- (void)photoSourceLoading:(id<T3PhotoSource>)aPhotoSource fromIndex:(NSUInteger)fromIndex
+- (void)photoSourceLoading:(id<T3PhotoSource>)photoSource fromIndex:(NSUInteger)fromIndex
toIndex:(NSUInteger)toIndex {
self.contentState |= T3ContentActivity;
}
-- (void)photoSourceLoaded:(id<T3PhotoSource>)aPhotoSource {
+- (void)photoSourceLoaded:(id<T3PhotoSource>)photoSource {
if (_photoSource.numberOfPhotos) {
self.contentState = T3ContentReady;
} else {
@@ -232,7 +232,7 @@ - (void)photoSourceLoaded:(id<T3PhotoSource>)aPhotoSource {
}
}
-- (void)photoSource:(id<T3PhotoSource>)aPhotoSource loadDidFailWithError:(NSError*)error {
+- (void)photoSource:(id<T3PhotoSource>)photoSource loadDidFailWithError:(NSError*)error {
self.contentState &= ~T3ContentActivity;
self.contentState |= T3ContentError;
self.contentError = error;
@@ -240,10 +240,10 @@ - (void)photoSource:(id<T3PhotoSource>)aPhotoSource loadDidFailWithError:(NSErro
///////////////////////////////////////////////////////////////////////////////////////////////////
-- (void)setPhotoSource:(id<T3PhotoSource>)aPhotoSource {
- if (aPhotoSource != _photoSource) {
+- (void)setPhotoSource:(id<T3PhotoSource>)photoSource {
+ if (photoSource != _photoSource) {
[_photoSource release];
- _photoSource = [aPhotoSource retain];
+ _photoSource = [photoSource retain];
[self invalidate];
}
View
720 src/T3URLCache.m
@@ -10,27 +10,39 @@
static const NSTimeInterval kFlushDelay = 0.3;
static const NSTimeInterval kTimeout = 300.0;
-static const int kLoadMaxRetries = 2;
-static const int kMaxConcurrentLoads = 5;
+static const NSInteger kLoadMaxRetries = 2;
+static const NSInteger kMaxConcurrentLoads = 5;
+static NSUInteger kMaxContentLength = 150000;
+
static NSString* kCacheDirPathName = @"Three20";
+static NSString* kSafariUserAgent = @"Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2 like Mac OS X;\
+ en-us) AppleWebKit/525.181 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20";
//////////////////////////////////////////////////////////////////////////////////////////////////
@interface T3RequestLoader : NSObject {
- NSString* url;
- T3URLCache* cache;
- NSMutableArray* requests;
- NSURLConnection* connection;
- NSInteger statusCode;
- NSString* contentType;
- BOOL convertMedia;
- int retriesLeft;
-}
-
+ NSString* _url;
+ NSString* _cacheKey;
+ T3URLCache* _cache;
+ T3URLRequestCachePolicy _cachePolicy;
+ NSMutableArray* _requests;
+ NSURLConnection* _connection;
+ NSMutableData* _responseData;
+ NSInteger _statusCode;
+ NSString* _contentType;
+ NSTimeInterval _cacheExpirationAge;
+ BOOL _shouldConvertToMedia;
+ int _retriesLeft;
+}
+
+@property(nonatomic,readonly) NSArray* requests;
@property(nonatomic,readonly) NSString* url;
+@property(nonatomic,readonly) NSString* cacheKey;
@property(nonatomic,readonly) NSString* contentType;
+@property(nonatomic,readonly) T3URLRequestCachePolicy cachePolicy;
+@property(nonatomic,readonly) NSTimeInterval cacheExpirationAge;
@property(nonatomic,readonly) BOOL loading;
-@property(nonatomic,readonly) BOOL convertMedia;
+@property(nonatomic,readonly) BOOL shouldConvertToMedia;
- (id)initForRequest:(T3URLRequest*)request cache:(T3URLCache*)cache;
@@ -44,31 +56,37 @@ - (BOOL)cancel:(T3URLRequest*)request;
@implementation T3RequestLoader
-@synthesize url, contentType, convertMedia;
+@synthesize url = _url, requests = _requests, contentType = _contentType, cacheKey = _cacheKey,
+ cachePolicy = _cachePolicy, cacheExpirationAge = _cacheExpirationAge,
+ shouldConvertToMedia = _shouldConvertToMedia;
-- (id)initForRequest:(T3URLRequest*)request cache:(T3URLCache*)aCache {
+- (id)initForRequest:(T3URLRequest*)request cache:(T3URLCache*)cache {
if (self = [super init]) {
- url = [request.url copy];
- cache = aCache;
- connection = nil;
- statusCode = 0;
- contentType = nil;
- convertMedia = NO;
- retriesLeft = kLoadMaxRetries;
- requests = [[NSMutableArray alloc] init];
+ _url = [request.url copy];
+ _cacheKey = [request.cacheKey copy];
+ _cachePolicy = request.cachePolicy;
+ _cacheExpirationAge = request.cacheExpirationAge;
+ _cache = cache;
+ _connection = nil;
+ _statusCode = 0;
+ _contentType = nil;
+ _shouldConvertToMedia = NO;
+ _retriesLeft = kLoadMaxRetries;
+ _responseData = nil;
+ _requests = [[NSMutableArray alloc] init];
[self addRequest:request];
}
return self;
}
- (void)dealloc {
- if (connection) {
- [connection cancel];
- [connection release];
- }
- [url release];
- [contentType release];
- [requests release];
+ [_connection cancel];
+ [_connection release];
+ [_responseData release];
+ [_url release];
+ [_cacheKey release];
+ [_contentType release];
+ [_requests release];
[super dealloc];
}
@@ -77,16 +95,61 @@ - (void)dealloc {
- (void)connect {
T3NetworkRequestStarted();
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
+ NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_url]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:kTimeout];
- // XXXjoe Set the User-Agent to something
- connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+ [urlRequest setValue:_cache.userAgent forHTTPHeaderField:@"User-Agent"];
+
+ if (_requests.count == 1) {
+ T3URLRequest* request = [_requests objectAtIndex:0];
+ [urlRequest setHTTPShouldHandleCookies:request.shouldHandleCookies];
+
+ NSString* method = request.httpMethod;
+ if (method) {
+ [urlRequest setHTTPMethod:method];
+ }
+
+ NSString* contentType = request.contentType;
+ if (contentType) {
+ [urlRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];
+ }
+
+ NSData* body = request.httpBody;
+ if (body) {
+ [urlRequest setHTTPBody:body];
+ }
+ }
+
+ _connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
+}
+
+- (void)cancel {
+ NSArray* requestsToCancel = [_requests copy];
+ for (id request in requestsToCancel) {
+ [self cancel:request];
+ }
+ [requestsToCancel release];
}
-- (void)dispatchData:(NSData*)data media:(id)media {
- for (int i = 0; i < requests.count; ++i) {
- T3URLRequest* request = [requests objectAtIndex:i];
+- (NSError*)validateData:(NSData*)data {
+ for (T3URLRequest* request in _requests) {
+ NSError* error = [request.handler request:request validateData:data];
+ if (error) {
+ return error;
+ }
+ }
+ return nil;
+}
+
+- (void)dispatchData:(NSData*)data media:(id)media timestamp:(NSDate*)timestamp {
+ for (T3URLRequest* request in [[_requests copy] autorelease]) {
+ request.timestamp = timestamp;
+ request.loading = NO;
+
+ if ([request.handler respondsToSelector:@selector(request:loadedData:media:)]) {
+ [request.handler request:request loadedData:data media:media];
+ }
+
if ([request.delegate respondsToSelector:@selector(request:loadedData:media:)]) {
[request.delegate request:request loadedData:data media:media];
}
@@ -94,8 +157,13 @@ - (void)dispatchData:(NSData*)data media:(id)media {
}
- (void)dispatchError:(NSError*)error {
- NSEnumerator* e = [requests objectEnumerator];
- for (T3URLRequest* request; request = [e nextObject]; ) {
+ for (T3URLRequest* request in [[_requests copy] autorelease]) {
+ request.loading = NO;
+
+ if ([request.handler respondsToSelector:@selector(request:didFailWithError:)]) {
+ [request.handler request:request didFailWithError:error];
+ }
+
if ([request.delegate respondsToSelector:@selector(request:didFailWithError:)]) {
[request.delegate request:request didFailWithError:error];
}
@@ -106,74 +174,96 @@ - (void)dispatchError:(NSError*)error {
// NSURLConnectionDelegate
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLResponse*)response {
- statusCode = response.statusCode;
- if (convertMedia) {
+ _statusCode = response.statusCode;
+ NSDictionary* headers = [response allHeaderFields];
+ int contentLength = [[headers objectForKey:@"Content-Length"] intValue];
+ if (contentLength > _cache.maxContentLength && _cache.maxContentLength) {
+ [self cancel];
+ }
+
+ if (_shouldConvertToMedia) {
NSDictionary* headers = [response allHeaderFields];
if (headers) {
- contentType = [[headers objectForKey:@"Content-Type"] retain];
+ _contentType = [[headers objectForKey:@"Content-Type"] retain];
}
}
+
+ _responseData = [[NSMutableData alloc] initWithCapacity:contentLength];
+}
+
+-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
+ [_responseData appendData:data];
}
-- (NSCachedURLResponse *)connection:(NSURLConnection *)aConnection
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse {
- if (statusCode == 200) {
- NSData* data = cachedResponse.data;
- [cache performSelector:@selector(loader:loadedData:) withObject:self withObject:data];
- } else {
- T3LOG(@" FAILED LOADING (%d) %@", statusCode, url);
- NSError* error = [NSError errorWithDomain:NSURLErrorDomain code:statusCode userInfo:nil];
- [cache performSelector:@selector(loader:didFailWithError:) withObject:self withObject:error];
- }
return nil;
}
--(void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
+-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
T3NetworkRequestStopped();
- [connection release];
- connection = nil;
+
+ //T3LOG(@"Loaded: %s", _responseData.bytes);
+
+ if (_statusCode == 200) {
+ [_cache performSelector:@selector(loader:loadedData:) withObject:self withObject:_responseData];
+ } else {
+ T3LOG(@" FAILED LOADING (%d) %@", _statusCode, _url);
+ NSError* error = [NSError errorWithDomain:NSURLErrorDomain code:_statusCode userInfo:nil];
+ [_cache performSelector:@selector(loader:didFailWithError:) withObject:self withObject:error];
+ }
+
+ [_responseData release];
+ _responseData = nil;
+ [_connection release];
+ _connection = nil;
}
-- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error {
- T3LOG(@" FAILED LOADING %@ FOR %@", url, error);
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ T3LOG(@" FAILED LOADING %@ FOR %@", _url, error);
T3NetworkRequestStopped();
- [connection release];
- connection = nil;
+ [_responseData release];
+ _responseData = nil;
+ [_connection release];
+ _connection = nil;
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCannotFindHost
- && retriesLeft) {
+ && _retriesLeft) {
// If there is a network error then we will wait and retry a few times just in case
// it was just a temporary blip in connectivity
- --retriesLeft;
+ --_retriesLeft;
[self connect];
} else {
- [cache performSelector:@selector(loader:didFailWithError:) withObject:self withObject:error];
+ [_cache performSelector:@selector(loader:didFailWithError:) withObject:self withObject:error];
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)loading {
- return !!connection;
+ return !!_connection;
}
- (void)addRequest:(T3URLRequest*)request {
- [requests addObject:request];
- if (request.convertMedia) {
- convertMedia = YES;
+ [_requests addObject:request];
+ if (request.shouldConvertToMedia) {
+ _shouldConvertToMedia = YES;
}
}
- (void)removeRequest:(T3URLRequest*)request {
- [requests removeObject:request];
+ [_requests removeObject:request];
}
- (void)load {
- if (!connection) {
- for (int i = 0; i < requests.count; ++i) {
- T3URLRequest* request = [requests objectAtIndex:i];
+ if (!_connection) {
+ for (int i = 0; i < _requests.count; ++i) {
+ T3URLRequest* request = [_requests objectAtIndex:i];
+ if ([request.handler respondsToSelector:@selector(requestLoading:)]) {
+ [request.handler requestLoading:request];
+ }
if ([request.delegate respondsToSelector:@selector(requestLoading:)]) {
[request.delegate requestLoading:request];
}
@@ -184,18 +274,26 @@ - (void)load {
}
- (BOOL)cancel:(T3URLRequest*)request {
- NSUInteger index = [requests indexOfObject:request];
+ NSUInteger index = [_requests indexOfObject:request];
if (index != NSNotFound) {
- [requests removeObjectAtIndex:index];
+ [_requests removeObjectAtIndex:index];
+
+ request.loading = NO;
+ if ([request.handler respondsToSelector:@selector(requestCancelled:)]) {
+ [request.handler requestCancelled:request];
+ }
if ([request.delegate respondsToSelector:@selector(requestCancelled:)]) {
[request.delegate requestCancelled:request];
}
}
- if (![requests count]) {
- if (connection) {
+ if (![_requests count]) {
+ [_cache performSelector:@selector(loaderDidCancel:wasLoading:) withObject:self
+ withObject:(id)!!_connection];
+ if (_connection) {
T3NetworkRequestStopped();
- [connection cancel];
+ [_connection cancel];
+ _connection = nil;
}
return NO;
} else {
@@ -209,9 +307,12 @@ - (BOOL)cancel:(T3URLRequest*)request {
@implementation T3URLCache
-@synthesize disableDiskCache, disableMediaCache, cachePath, paused;
+@synthesize disableDiskCache = _disableDiskCache, disableMediaCache = _disableMediaCache,
+ cachePath = _cachePath, userAgent = _userAgent,
+ maxContentLength = _maxContentLength, maxPixelCount = _maxPixelCount,
+ invalidationAge = _invalidationAge, paused = _paused;
-+ (T3URLCache*) sharedCache {
++ (T3URLCache*)sharedCache {
static T3URLCache* sharedCache = nil;
if (!sharedCache) {
sharedCache = [[T3URLCache alloc] init];
@@ -237,19 +338,22 @@ + (NSString*)defaultCachePath {
- (id)init {
if (self == [super init]) {
- loaders = [[NSMutableDictionary alloc] init];
- loaderQueue = [[NSMutableArray alloc] init];
- mediaCache = [[NSMutableDictionary alloc] init];
- mediaSortedList = [[NSMutableArray alloc] init];
- loaderQueueTimer = nil;
- totalLoading = 0;
- disableDiskCache = NO;
- disableMediaCache = NO;
- paused = NO;
+ _loaders = [[NSMutableDictionary alloc] init];
+ _loaderQueue = [[NSMutableArray alloc] init];
+ _mediaCache = [[NSMutableDictionary alloc] init];
+ _mediaSortedList = [[NSMutableArray alloc] init];
+ _loaderQueueTimer = nil;
+ _userAgent = [kSafariUserAgent copy];
+ _totalLoading = 0;
+ _disableDiskCache = NO;
+ _disableMediaCache = NO;
+ _maxContentLength = kMaxContentLength;
+ _maxPixelCount = (SMALL_IMAGE_SIZE*20) + (MEDIUM_IMAGE_SIZE*12);
+ _invalidationAge = 0;
+ _paused = NO;
- totalPixelCount = 0;
- maxPixelCount = (SMALL_IMAGE_SIZE*20) + (MEDIUM_IMAGE_SIZE*12);
- cachePath = [[T3URLCache defaultCachePath] retain];
+ _totalPixelCount = 0;
+ _cachePath = [[T3URLCache defaultCachePath] retain];
// Disable the built-in cache to save memory
NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0
@@ -261,19 +365,20 @@ - (id)init {
}
- (void)dealloc {
- [loaders release];
- [loaderQueue release];
- [loaderQueueTimer invalidate];
- [mediaCache release];
- [mediaSortedList release];
- [cachePath release];
+ [_loaders release];
+ [_loaderQueue release];
+ [_loaderQueueTimer invalidate];
+ [_userAgent release];
+ [_mediaCache release];
+ [_mediaSortedList release];
+ [_cachePath release];
[super dealloc];
}
//////////////////////////////////////////////////////////////////////////////////////////////////
-- (NSString *)makeHashKey:(NSString*)input {
- const char* str = [input UTF8String];
+- (NSString *)generateCacheKey:(NSString*)url {
+ const char* str = [url UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, strlen(str), result);
@@ -284,6 +389,26 @@ - (NSString *)makeHashKey:(NSString*)input {
];
}
+- (NSString*)getCachePathForKey:(NSString*)key {
+ return [_cachePath stringByAppendingPathComponent:key];
+}
+
+- (void)expireImagesFromMemory {
+ while (_mediaSortedList.count) {
+ NSString* url = [_mediaSortedList objectAtIndex:0];
+ UIImage* image = [_mediaCache objectForKey:url];
+ T3LOG(@"EXPIRING %@", url);
+
+ _totalPixelCount -= image.size.width * image.size.height;
+ [_mediaCache removeObjectForKey:url];
+ [_mediaSortedList removeObjectAtIndex:0];
+
+ if (_totalPixelCount <= _maxPixelCount) {
+ break;
+ }
+ }
+}
+
- (NSData*)loadDataFromDisk:(NSString*)url {
NSString* filePath = [self getCachePathForURL:url];
NSFileManager* fm = [NSFileManager defaultManager];
@@ -294,37 +419,45 @@ - (NSData*)loadDataFromDisk:(NSString*)url {
}
}
-- (void)writeDataToDisk:(NSData*)imageData withType:(NSString*)type forURL:(NSString*)url {
- NSString* filePath = [self getCachePathForURL:url];
+- (void)writeDataToDisk:(NSData*)imageData withType:(NSString*)type forKey:(NSString*)key {
+ NSString* filePath = [self getCachePathForKey:key];
NSFileManager* fm = [NSFileManager defaultManager];
[fm createFileAtPath:filePath contents:imageData attributes:nil];
}
-- (void)writeImageToDisk:(UIImage*)image forURL:(NSString*)url {
+- (void)writeImageToDisk:(UIImage*)image forKey:(NSString*)key {
NSData* imageData = UIImagePNGRepresentation(image);
- [self writeDataToDisk:imageData withType:@"image" forURL:url];
-}
-
-- (void)removeFromDisk:(NSString*)url {
- NSString* filePath = [self getCachePathForURL:url];
- NSFileManager* fm = [NSFileManager defaultManager];
- if (filePath && [fm fileExistsAtPath:filePath]) {
- [fm removeItemAtPath:filePath error:nil];
- }
+ [self writeDataToDisk:imageData withType:@"image" forKey:key];
}
-- (void)expireImagesFromMemory {
- while (mediaSortedList.count) {
- NSString* url = [mediaSortedList objectAtIndex:0];
- UIImage* image = [mediaCache objectForKey:url];
- T3LOG(@"Expiring %@", url);
-
- totalPixelCount -= image.size.width * image.size.height;
- [mediaCache removeObjectForKey:url];
- [mediaSortedList removeObjectAtIndex:0];
+- (void)storeData:(NSData*)data media:(id)media forURL:(NSString*)url key:(NSString*)key
+ toDisk:(BOOL)toDisk {
+ if (!_disableMediaCache && media) {
+ if ([media isKindOfClass:[UIImage class]]) {
+ UIImage* image = media;
+ int pixelCount = image.size.width * image.size.height;
+ if (pixelCount < LARGE_IMAGE_SIZE) {
+ _totalPixelCount += pixelCount;
+ if (_totalPixelCount > _maxPixelCount && _maxPixelCount) {
+ [self expireImagesFromMemory];
+ }
- if (totalPixelCount <= maxPixelCount) {
- break;
+ // T3LOG(@"CACHING IMAGE %@", url);
+ [_mediaSortedList addObject:url];
+ [_mediaCache setObject:image forKey:url];
+ }
+ }
+ }
+
+ if (toDisk && !_disableDiskCache) {
+ if ([media isKindOfClass:[UIImage class]]) {
+ if (data) {
+ [self writeDataToDisk:data withType:@"image" forKey:key];
+ } else {
+ [self writeImageToDisk:media forKey:key];
+ }
+ } else if (data) {
+ [self writeDataToDisk:data withType:nil forKey:key];
}
}
}
@@ -362,9 +495,10 @@ - (id)convertDataToMedia:(NSData*)data forURL:(NSString*)url {
return image;
}
-- (BOOL)loadFromCache:(NSString*)url convertMedia:(BOOL)convertMedia data:(NSData**)data
- media:(id*)media fromDisk:(BOOL)fromDisk {
- if (convertMedia) {
+- (BOOL)loadFromCache:(NSString*)url cacheKey:(NSString*)cacheKey
+ expires:(NSTimeInterval)expirationAge shouldConvertToMedia:(BOOL)shouldConvertToMedia
+ fromDisk:(BOOL)fromDisk data:(NSData**)data media:(id*)media timestamp:(NSDate**)timestamp {
+ if (shouldConvertToMedia) {
*media = [self getMediaForURL:url fromDisk:NO];
if (*media) {
return YES;
@@ -372,12 +506,56 @@ - (BOOL)loadFromCache:(NSString*)url convertMedia:(BOOL)convertMedia data:(NSDat
}
if (fromDisk) {
- *data = [self loadDataFromDisk:url];
+ *data = [self getDataForKey:cacheKey expires:expirationAge timestamp:timestamp];
if (*data) {
- if (convertMedia) {
+ if (shouldConvertToMedia) {
*media = [self convertDataToMedia:*data forURL:url];
[self storeData:nil media:*media forURL:url toDisk:NO];
}
+
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+- (BOOL)loadRequestFromCache:(T3URLRequest*)request {
+ if (request.cachePolicy & (T3URLRequestCachePolicyDisk|T3URLRequestCachePolicyMemory)) {
+ NSData* data = nil;
+ id media = nil;
+ NSDate* timestamp = nil;
+ BOOL delayed = (_paused && request.canBeDelayed) || _totalLoading == kMaxConcurrentLoads;
+
+ if ([self loadFromCache:request.url cacheKey:request.cacheKey
+ expires:request.cacheExpirationAge
+ shouldConvertToMedia:request.shouldConvertToMedia
+ fromDisk:!delayed && request.cachePolicy & T3URLRequestCachePolicyDisk
+ data:&data media:&media timestamp:&timestamp]) {
+
+ NSError* error = [request.handler request:request validateData:data];
+ if (error) {
+ if ([request.handler respondsToSelector:@selector(request:didFailWithError:)]) {
+ [request.handler request:request didFailWithError:error];
+ }
+
+ if ([request.delegate respondsToSelector:@selector(request:didFailWithError:)]) {
+ [request.delegate request:request didFailWithError:error];
+ }
+ } else {
+ request.responseFromCache = YES;
+ request.timestamp = timestamp;
+ request.loading = NO;
+
+ if ([request.handler respondsToSelector:@selector(request:loadedData:media:)]) {
+ [request.handler request:request loadedData:data media:media];
+ }
+
+ if ([request.delegate respondsToSelector:@selector(request:loadedData:media:)]) {
+ [request.delegate request:request loadedData:data media:media];
+ }
+ }
+
return YES;
}
}
@@ -388,42 +566,54 @@ - (BOOL)loadFromCache:(NSString*)url convertMedia:(BOOL)convertMedia data:(NSDat
- (void)executeLoader:(T3RequestLoader*)loader {
NSData* data = nil;
id media = nil;
- if ([self loadFromCache:loader.url convertMedia:loader.convertMedia data:&data media:&media
- fromDisk:YES]) {
- [loader dispatchData:data media:media];
- [loaders removeObjectForKey:loader.url];
+ NSDate* timestamp = nil;
+ BOOL canUseCache = loader.cachePolicy
+ & (T3URLRequestCachePolicyDisk|T3URLRequestCachePolicyMemory);
+
+ if (canUseCache && [self loadFromCache:loader.url cacheKey:loader.cacheKey
+ expires:loader.cacheExpirationAge shouldConvertToMedia:loader.shouldConvertToMedia
+ fromDisk:loader.cachePolicy & T3URLRequestCachePolicyDisk
+ data:&data media:&media timestamp:&timestamp]) {
+ NSError* error = [loader validateData:data];
+ if (error) {
+ [loader dispatchError:error];
+ } else {
+ [loader dispatchData:data media:media timestamp:timestamp];
+ }
+
+ [_loaders removeObjectForKey:loader.cacheKey];
} else {
- ++totalLoading;
+ ++_totalLoading;
[loader load];
}
}
- (void)loadNextInQueueDelayed {
- if (!loaderQueueTimer) {
- loaderQueueTimer = [NSTimer scheduledTimerWithTimeInterval:kFlushDelay target:self
+ if (!_loaderQueueTimer) {
+ _loaderQueueTimer = [NSTimer scheduledTimerWithTimeInterval:kFlushDelay target:self
selector:@selector(loadNextInQueue) userInfo:nil repeats:NO];
}
}
- (void)loadNextInQueue {
- loaderQueueTimer = nil;
+ _loaderQueueTimer = nil;
- for (int i = 0; i < kMaxConcurrentLoads && totalLoading < kMaxConcurrentLoads
- && loaderQueue.count; ++i) {
- T3RequestLoader* loader = [[loaderQueue objectAtIndex:0] retain];
- [loaderQueue removeObjectAtIndex:0];
+ for (int i = 0; i < kMaxConcurrentLoads && _totalLoading < kMaxConcurrentLoads
+ && _loaderQueue.count; ++i) {
+ T3RequestLoader* loader = [[_loaderQueue objectAtIndex:0] retain];
+ [_loaderQueue removeObjectAtIndex:0];
[self executeLoader:loader];
[loader release];
}
- if (loaderQueue.count) {
+ if (_loaderQueue.count) {
[self loadNextInQueueDelayed];
}
}
- (void)loadNextInQueueAfterLoader:(T3RequestLoader*)loader {
- --totalLoading;
- [loaders removeObjectForKey:loader.url];
+ --_totalLoading;
+ [_loaders removeObjectForKey:loader.cacheKey];
[self loadNextInQueue];
}
@@ -433,65 +623,85 @@ - (void)loader:(T3RequestLoader*)loader didFailWithError:(NSError*)error {
}
- (void)loader:(T3RequestLoader*)loader loadedData:(NSData*)data {
- id media = nil;
- if (loader.convertMedia) {
- media = [self convertDataToMedia:data forType:loader.contentType];
- if (!media) {
- return [self loader:loader didFailWithError:nil];
- return;
+ NSError* error = [loader validateData:data];
+ if (error) {
+ [loader dispatchError:error];
+ } else {
+ id media = nil;
+ if (loader.shouldConvertToMedia) {
+ media = [self convertDataToMedia:data forType:loader.contentType];
+ if (!media) {
+ return [self loader:loader didFailWithError:nil];
+ }
}
- }
- [self storeData:data media:media forURL:loader.url toDisk:YES];
- [loader dispatchData:data media:media];
+ if (!(loader.cachePolicy & T3URLRequestCachePolicyNoCache)) {
+ [self storeData:data media:media forURL:loader.url key:loader.cacheKey toDisk:YES];
+ }
+ [loader dispatchData:data media:media timestamp:[NSDate date]];
+ }
[self loadNextInQueueAfterLoader:loader];
}
+- (void)loaderDidCancel:(T3RequestLoader*)loader wasLoading:(BOOL)wasLoading {
+ if (wasLoading) {
+ [self loadNextInQueueAfterLoader:loader];
+ } else {
+ [_loaders removeObjectForKey:loader.cacheKey];
+ }
+}
+
//////////////////////////////////////////////////////////////////////////////////////////////////
- (void)setPaused:(BOOL)isPaused {
- T3LOG(@"PAUSE CACHE %d", isPaused);
- paused = isPaused;
+ // T3LOG(@"PAUSE CACHE %d", isPaused);
+ _paused = isPaused;
- if (!paused) {
+ if (!_paused) {
[self loadNextInQueue];
- } else if (loaderQueueTimer) {
- [loaderQueueTimer invalidate];
- loaderQueueTimer = nil;
+ } else if (_loaderQueueTimer) {
+ [_loaderQueueTimer invalidate];
+ _loaderQueueTimer = nil;
}
}
- (BOOL)sendRequest:(T3URLRequest*)request {
- // First, look in the cache if the request allows it
- NSData* data = nil;
- id media = nil;
- if ([self loadFromCache:request.url convertMedia:request.convertMedia data:&data media:&media
- fromDisk:!paused && totalLoading != kMaxConcurrentLoads]) {
- if ([request.delegate respondsToSelector:@selector(request:loadedData:media:)]) {
- [request.delegate request:request loadedData:data media:media];
- }
- return YES;
+ if (!request.cacheKey) {
+ request.cacheKey = [self generateCacheKey:request.url];
}
-
+
+ if ([request.handler respondsToSelector:@selector(requestPosted:)]) {
+ [request.handler requestPosted:request];
+ }
+
if ([request.delegate respondsToSelector:@selector(requestPosted:)]) {
[request.delegate requestPosted:request];
}
- // Next, see if there is an active loader for the URL and if so join that bandwagon
- T3RequestLoader* loader = [loaders objectForKey:request.url];
- if (loader) {
- [loader addRequest:request];
- return NO;
+ if ([self loadRequestFromCache:request]) {
+ return YES;
+ }
+
+ request.loading = YES;
+
+ T3RequestLoader* loader = nil;
+ if (![request.httpMethod isEqualToString:@"POST"]) {
+ // Next, see if there is an active loader for the URL and if so join that bandwagon
+ loader = [_loaders objectForKey:request.cacheKey];
+ if (loader) {
+ [loader addRequest:request];
+ return NO;
+ }
}
// Finally, create a new loader and hit the network (unless we are paused)
loader = [[T3RequestLoader alloc] initForRequest:request cache:self];
- [loaders setObject:loader forKey:request.url];
- if (paused || totalLoading == kMaxConcurrentLoads) {
- [loaderQueue addObject:loader];
+ [_loaders setObject:loader forKey:request.cacheKey];
+ if ((_paused && request.canBeDelayed) || _totalLoading == kMaxConcurrentLoads) {
+ [_loaderQueue addObject:loader];
} else {
- ++totalLoading;
+ ++_totalLoading;
[loader load];
}
[loader release];
@@ -501,25 +711,44 @@ - (BOOL)sendRequest:(T3URLRequest*)request {
- (void)cancelRequest:(T3URLRequest*)request {
if (request) {
- T3RequestLoader* loader = [loaders objectForKey:request.url];
+ T3RequestLoader* loader = [_loaders objectForKey:request.cacheKey];
if (loader) {
- [request retain];
- BOOL wasLoading = loader.loading;
if (![loader cancel:request]) {
- if (wasLoading) {
- --totalLoading;
+ [_loaderQueue removeObject:loader];
+ }
+ }
+ }
+}
+
+- (void)cancelRequestsWithDelegate:(id)delegate {
+ NSMutableArray* requestsToCancel = nil;
+
+ for (T3RequestLoader* loader in [_loaders objectEnumerator]) {
+ for (T3URLRequest* request in loader.requests) {
+ if (request.delegate == delegate || request.handler == delegate
+ || request.handlerDelegate == delegate) {
+ if (!requestsToCancel) {
+ requestsToCancel = [NSMutableArray array];
}
- [loaders removeObjectForKey:request.url];
- [loaderQueue removeObject:loader];
+ [requestsToCancel addObject:request];
}
- [request release];
}
}
+
+ for (T3URLRequest* request in requestsToCancel) {
+ [self cancelRequest:request];
+ }
+}
+
+- (void)cancelAllRequests {
+ for (T3RequestLoader* loader in [[[_loaders copy] autorelease] objectEnumerator]) {
+ [loader cancel];
+ }
}
- (NSString*)getCachePathForURL:(NSString*)url {
- NSString* key = [self makeHashKey:url];
- return [cachePath stringByAppendingPathComponent:key];
+ NSString* key = [self generateCacheKey:url];
+ return [self getCachePathForKey:key];
}
- (BOOL)hasDataForURL:(NSString*)url {
@@ -529,28 +758,32 @@ - (BOOL)hasDataForURL:(NSString*)url {
}
- (NSData*)getDataForURL:(NSString*)url {
- return [self getDataForURL:url minTime:T3_ALWAYS_USE_CACHE timestamp:nil];
+ return [self getDataForURL:url expires:0 timestamp:nil];
}
-- (NSData*)getDataForURL:(NSString*)url minTime:(NSTimeInterval)minTime
+- (NSData*)getDataForURL:(NSString*)url expires:(NSTimeInterval)expirationAge
timestamp:(NSDate**)timestamp {
- if (minTime != T3_SKIP_CACHE) {
- NSString* filePath = [self getCachePathForURL:url];
- NSFileManager* fm = [NSFileManager defaultManager];
- if ([fm fileExistsAtPath:filePath]) {
- if (minTime != T3_ALWAYS_USE_CACHE) {
- NSDictionary* attrs = [fm attributesOfItemAtPath:filePath error:nil];
- NSDate* modified = [attrs objectForKey:NSFileModificationDate];
- if ([modified timeIntervalSinceNow] < -minTime) {
- return nil;
- }
- if (timestamp) {
- *timestamp = modified;
- }
- }
- return [NSData dataWithContentsOfFile:filePath];
+ NSString* key = [self generateCacheKey:url];
+ return [self getDataForKey:key expires:expirationAge timestamp:timestamp];
+}
+
+- (NSData*)getDataForKey:(NSString*)key expires:(NSTimeInterval)expirationAge
+ timestamp:(NSDate**)timestamp {
+ NSString* filePath = [self getCachePathForKey:key];
+ NSFileManager* fm = [NSFileManager defaultManager];
+ if ([fm fileExistsAtPath:filePath]) {
+ NSDictionary* attrs = [fm attributesOfItemAtPath:filePath error:nil];
+ NSDate* modified = [attrs objectForKey:NSFileModificationDate];
+ if (expirationAge && [modified timeIntervalSinceNow] < -expirationAge) {
+ return nil;
+ }
+ if (timestamp) {
+ *timestamp = modified;
}
+
+ return [NSData dataWithContentsOfFile:filePath];
}
+
return nil;
}
@@ -559,7 +792,7 @@ - (id)getMediaForURL:(NSString*)url {
}
- (id)getMediaForURL:(NSString*)url fromDisk:(BOOL)fromDisk {
- UIImage* media = [mediaCache objectForKey:url];
+ UIImage* media = [_mediaCache objectForKey:url];
if (media) {
return [[media retain] autorelease];
} else if (fromDisk) {
@@ -571,34 +804,8 @@ - (id)getMediaForURL:(NSString*)url fromDisk:(BOOL)fromDisk {
}
- (void)storeData:(NSData*)data media:(id)media forURL:(NSString*)url toDisk:(BOOL)toDisk {
- if (!disableMediaCache && media) {
- if ([media isKindOfClass:[UIImage class]]) {
- UIImage* image = media;
- int pixelCount = image.size.width * image.size.height;
- if (pixelCount < LARGE_IMAGE_SIZE) {
- totalPixelCount += pixelCount;
- if (totalPixelCount > maxPixelCount) {
- [self expireImagesFromMemory];
- }
-
- T3LOG(@"CACHING IMAGE %@", url);
- [mediaSortedList addObject:url];
- [mediaCache setObject:image forKey:url];
- }
- }
- }
-
- if (toDisk && !disableDiskCache) {
- if ([media isKindOfClass:[UIImage class]]) {
- if (data) {
- [self writeDataToDisk:data withType:@"image" forURL:url];
- } else {
- [self writeImageToDisk:media forURL:url];
- }
- } else if (data) {
- [self writeDataToDisk:data withType:nil forURL:url];
- }
- }
+ NSString* key = [self generateCacheKey:url];
+ [self storeData:data media:media forURL:url key:key toDisk:toDisk];
}
- (NSString*)storeTemporaryData:(NSData*)data media:(id)media toDisk:(BOOL)toDisk {
@@ -612,10 +819,10 @@ - (NSString*)storeTemporaryData:(NSData*)data media:(id)media toDisk:(BOOL)toDis
- (void)moveDataForURL:(NSString*)oldURL toURL:(NSString*)newURL {
id media = [self getMediaForURL:oldURL fromDisk:NO];
if (media) {
- [mediaSortedList removeObject:oldURL];
- [mediaCache removeObjectForKey:oldURL];
- [mediaSortedList addObject:newURL];
- [mediaCache setObject:media forKey:newURL];
+ [_mediaSortedList removeObject:oldURL];
+ [_mediaCache removeObjectForKey:oldURL];
+ [_mediaSortedList addObject:newURL];
+ [_mediaCache setObject:media forKey:newURL];
}
NSString* oldPath = [self getCachePathForURL:oldURL];
NSFileManager* fm = [NSFileManager defaultManager];
@@ -626,31 +833,48 @@ - (void)moveDataForURL:(NSString*)oldURL toURL:(NSString*)newURL {
}
- (void)removeURL:(NSString*)url fromDisk:(BOOL)fromDisk {
- [mediaSortedList removeObject:url];
- [mediaCache removeObjectForKey:url];
+ [_mediaSortedList removeObject:url];
+ [_mediaCache removeObjectForKey:url];
if (fromDisk) {
- [self removeFromDisk:url];
+ NSString* filePath = [self getCachePathForURL:url];
+ NSFileManager* fm = [NSFileManager defaultManager];
+ if (filePath && [fm fileExistsAtPath:filePath]) {
+ [fm removeItemAtPath:filePath error:nil];
+ }
+ }
+}
+
+- (void)removeKey:(NSString*)key {
+ NSString* filePath = [self getCachePathForKey:key];
+ NSFileManager* fm = [NSFileManager defaultManager];
+ if (filePath && [fm fileExistsAtPath:filePath]) {
+ [fm removeItemAtPath:filePath error:nil];
}
}
- (void)removeAll:(BOOL)fromDisk {
- [mediaCache removeAllObjects];
- [mediaSortedList removeAllObjects];
- totalPixelCount = 0;
+ [_mediaCache removeAllObjects];
+ [_mediaSortedList removeAllObjects];
+ _totalPixelCount = 0;
if (fromDisk) {
NSFileManager* fm = [NSFileManager defaultManager];
- [fm removeItemAtPath:cachePath error:nil];
- [fm createDirectoryAtPath:cachePath attributes:nil];
+ [fm removeItemAtPath:_cachePath error:nil];
+ [fm createDirectoryAtPath:_cachePath attributes:nil];
}
}
- (void)invalidateURL:(NSString*)url {
- NSString* filePath = [self getCachePathForURL:url];
+ NSString* key = [self generateCacheKey:url];
+ return [self invalidateKey:key];
+}
+
+- (void)invalidateKey:(NSString*)key {
+ NSString* filePath = [self getCachePathForKey:key];
NSFileManager* fm = [NSFileManager defaultManager];
if (filePath && [fm fileExistsAtPath:filePath]) {
- NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-T3_DEFAULT_CACHE_AGE];
+ NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-_invalidationAge];
NSDictionary* attrs = [NSDictionary dictionaryWithObject:invalidDate
forKey:NSFileModificationDate];
@@ -659,23 +883,23 @@ - (void)invalidateURL:(NSString*)url {
}
- (void)invalidateAll {
- NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-T3_DEFAULT_CACHE_AGE];
+ NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-_invalidationAge];
NSDictionary* attrs = [NSDictionary dictionaryWithObject:invalidDate
forKey:NSFileModificationDate];
NSFileManager* fm = [NSFileManager defaultManager];
- NSDirectoryEnumerator* e = [fm enumeratorAtPath:cachePath];
+ NSDirectoryEnumerator* e = [fm enumeratorAtPath:_cachePath];
for (NSString* fileName; fileName = [e nextObject]; ) {
- NSString* filePath = [cachePath stringByAppendingPathComponent:fileName];
+ NSString* filePath = [_cachePath stringByAppendingPathComponent:fileName];
[fm changeFileAttributes:attrs atPath:filePath];
}
}
- (void)logMemoryReport {
- T3LOG(@"======= IMAGE CACHE: %d media, %d pixels ========", mediaCache.count, totalPixelCount);
- NSEnumerator* e = [mediaCache keyEnumerator];
+ T3LOG(@"======= IMAGE CACHE: %d media, %d pixels ========", _mediaCache.count, _totalPixelCount);
+ NSEnumerator* e = [_mediaCache keyEnumerator];
for (NSString* url ; url = [e nextObject]; ) {
- id media = [mediaCache objectForKey:url];
+ id media = [_mediaCache objectForKey:url];
if ([media isKindOfClass:[UIImage class]]) {
UIImage* image = media;
T3LOG(@" %f x %f %@", image.size.width, image.size.height, url);
View
52 src/T3URLRequest.m
@@ -5,27 +5,61 @@
@implementation T3URLRequest
-@synthesize url, delegate, minTime, convertMedia;
+@synthesize delegate = _delegate, handler = _handler, handlerDelegate = _handlerDelegate,
+ url = _url, httpMethod = _httpMethod, httpBody = _httpBody, contentType = _contentType,
+ cachePolicy = _cachePolicy, cacheExpirationAge = _cacheExpirationAge, cacheKey = _cacheKey,
+ timestamp = _timestamp, loading = _loading, canBeDelayed = _canBeDelayed,
+ shouldHandleCookies = _shouldHandleCookies, shouldConvertToMedia = _shouldConvertToMedia,
+ responseFromCache = _responseFromCache;
-+ (T3URLRequest*)requestWithURL:(NSString*)url delegate:(id<T3URLRequestDelegate>)aDelegate {
- return [[[T3URLRequest alloc] initWithURL:url delegate:aDelegate] autorelease];
++ (T3URLRequest*)requestWithURL:(NSString*)url delegate:(id<T3URLRequestDelegate>)delegate {
+ return [[[T3URLRequest alloc] initWithURL:url delegate:delegate] autorelease];
}
-- (id)initWithURL:(NSString*)aURL delegate:(id<T3URLRequestDelegate>)aDelegate {
+- (id)initWithURL:(NSString*)url delegate:(id<T3URLRequestDelegate>)delegate {
+ if (self = [self init]) {
+ _url = [url retain];
+ _delegate = delegate;
+ }
+ return self;
+}
+
+- (id)init {
if (self = [super init]) {
- url = [aURL retain];
- delegate = aDelegate;
- convertMedia = NO;
- minTime = T3_DEFAULT_CACHE_AGE;
+ _url = nil;
+ _httpMethod = nil;
+ _httpBody = nil;
+ _contentType = nil;
+ _delegate = nil;
+ _handler = nil;
+ _handlerDelegate = nil;
+ _cachePolicy = T3URLRequestCachePolicyAny;
+ _shouldConvertToMedia = NO;
+ _cacheExpirationAge = 0;
+ _loading = NO;
+ _shouldHandleCookies = YES;
+ _canBeDelayed = YES;
+ _shouldConvertToMedia = NO;
+ _responseFromCache = NO;
}
return self;
}
- (void)dealloc {
- [url release];
+ [_url release];
+ [_httpMethod release];
+ [_httpBody release];
+ [_contentType release];
+ [_handler release];
[super dealloc];
}
+- (NSString*)description {
+ return [NSString stringWithFormat:@"<T3URLRequest %@>", _url];
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
- (BOOL)send {
return [[T3URLCache sharedCache] sendRequest:self];
}
View
5 src/T3UnclippedView.m
@@ -3,10 +3,9 @@
@implementation T3UnclippedView
- (void)didMoveToSuperview {
- // This allows the view to be shown "full screen", and not offset by the toolbar and status bar
- self.superview.clipsToBounds = NO;
-
for (UIView* p = self; p; p = p.superview) {
+ // This allows the view to be shown "full screen", and not offset by the toolbar and status bar
+ p.clipsToBounds = NO;
p.backgroundColor = self.backgroundColor;
}
}
View
3  src/T3ViewController.m
@@ -26,6 +26,9 @@ - (id)init {
- (void)dealloc {
T3LOG(@"DEALLOC %@", self);
+
+ [[T3URLCache sharedCache] cancelRequestsWithDelegate:self];
+
[_viewState release];
[_contentError release];
View
16 src/T3YouTubeView.m
@@ -14,17 +14,17 @@
@implementation T3YouTubeView
-@synthesize url;
+@synthesize url = _url;
-- (id)initWithURL:(NSString*)aURL {
+- (id)initWithURL:(NSString*)url {
if (self = [self initWithFrame:CGRectMake(0, 0, kDefaultWidth, kDefaultHeight)]) {
- self.url = aURL;
+ self.url = url;
}
return self;
}
- (void)dealloc {
- [url release];
+ [_url release];
[super dealloc];
}
@@ -38,11 +38,11 @@ - (void)layoutSubviews {
///////////////////////////////////////////////////////////////////////////////////////////////////
-- (void)setUrl:(NSString*)aURL {
- [url release];
- url = [aURL copy];
+- (void)setUrl:(NSString*)url {
+ [_url release];
+ _url = [url copy];
- NSString* html = [NSString stringWithFormat:kEmbedHTML, url, self.width, self.height];
+ NSString* html = [NSString stringWithFormat:kEmbedHTML, _url, self.width, self.height];
[self loadHTMLString:html baseURL:nil];
}
View
6 src/Three20/T3Global.h
@@ -53,13 +53,11 @@ typedef enum {
T3URLRequestCachePolicyDisk = 2,
T3URLRequestCachePolicyNetwork = 4,
T3URLRequestCachePolicyAny
- = (T3URLRequestCachePolicyMemory|T3URLRequestCachePolicyDisk|T3URLRequestCachePolicyNetwork)
+ = (T3URLRequestCachePolicyMemory|T3URLRequestCachePolicyDisk|T3URLRequestCachePolicyNetwork),
+ T3URLRequestCachePolicyNoCache = 8,
} T3URLRequestCachePolicy;
-#define T3_SKIP_CACHE CGFLOAT_MAX
-#define T3_ALWAYS_USE_CACHE 0
#define T3_DEFAULT_CACHE_AGE (60*3)
-#define T3_LONG_CACHE_AGE (60*60*24*7)
///////////////////////////////////////////////////////////////////////////////////////////////////
View
1  src/Three20/T3PhotoViewController.h
@@ -16,6 +16,7 @@
UIImage* _defaultImage;
NSString* _statusText;
UIBarStyle _previousBarStyle;
+ UIColor* _previousBarTintColor;
NSTimer* _loadTimer;
BOOL _delayLoad;
}
View
10 src/Three20/T3ThumbsTableViewCell.h
@@ -4,11 +4,11 @@
@protocol T3Photo;
@interface T3ThumbsTableViewCell : UITableViewCell {
- id<T3Photo> photo;
- T3ThumbView* thumbView1;
- T3ThumbView* thumbView2;
- T3ThumbView* thumbView3;
- T3ThumbView* thumbView4;
+ id<T3Photo> _photo;
+ T3ThumbView* _thumbView1;
+ T3ThumbView* _thumbView2;
+ T3ThumbView* _thumbView3;
+ T3ThumbView* _thumbView4;
}
@property(nonatomic,retain) id<T3Photo> photo;
View
84 src/Three20/T3URLCache.h
@@ -4,18 +4,22 @@
@class T3URLRequest;
@interface T3URLCache : NSObject {
- NSString* cachePath;
- NSMutableDictionary* loaders;
- NSMutableArray* loaderQueue;
- NSMutableDictionary* mediaCache;
- NSMutableArray* mediaSortedList;
- NSUInteger totalPixelCount;
- NSUInteger maxPixelCount;
- NSUInteger totalLoading;
- NSTimer* loaderQueueTimer;
- BOOL disableDiskCache;
- BOOL disableMediaCache;
- BOOL paused;
+ NSString* _cachePath;
+ NSMutableDictionary* _loaders;
+ NSMutableArray* _loaderQueue;
+ NSMutableDictionary* _mediaCache;
+ NSMutableArray* _mediaSortedList;
+ NSUInteger _totalPixelCount;
+ NSUInteger _maxPixelCount;
+ NSInteger _totalLoading;
+ NSTimer* _loaderQueueTimer;
+ NSString* _userAgent;
+ NSString* _baseURL;
+ NSUInteger _maxContentLength;
+ NSTimeInterval _invalidationAge;
+ BOOL _disableDiskCache;
+ BOOL _disableMediaCache;
+ BOOL _paused;
}
/**
@@ -43,6 +47,35 @@
@property(nonatomic,copy) NSString* cachePath;
/**
+ * The user agent string that will be used for all requests.
+ *
+ * The default value is the user agent string used by Safari.
+ */
+@property(nonatomic,copy) NSString* userAgent;
+
+/**
+ * The maximum size of a download that is allowed.
+ *
+ * If a response reports a content length greater than the max, the download will be
+ * cancelled. This is helpful for preventing excessive memory usage. Setting this to
+ * zero will allow all downloads regardless of size. The default is a relatively large value.
+ */
+@property(nonatomic) NSUInteger maxContentLength;
+
+/**
+ * The maximum number of pixels to keep in memory for cached images.
+ *
+ * Setting this to zero will allow an unlimited number of images to be cached. The default
+ * is enough to hold roughly 25 small images.
+ */
+@property(nonatomic) NSUInteger maxPixelCount;
+
+/**
+ * The amount of time to set back the modification timestamp on files when invalidating them.
+ */
+@property(nonatomic) NSTimeInterval invalidationAge;
+
+/**
* Gets the shared cache singleton used across the application.
*/
+ (T3URLCache*) sharedCache;
@@ -65,6 +98,19 @@
- (void)cancelRequest:(T3URLRequest*)request;
/**
+ * Cancels all active or pending requests whose delegate or handler is an object.
+ *
+ * This is useful for when an object is about to be destroyed and you want to remove pointers
+ * to it from active requests to prevent crashes when those pointers are later referenced.
+ */
+- (void)cancelRequestsWithDelegate:(id)delegate;
+
+/**
+ * Cancel all active or pending requests.
+ */
+- (void)cancelAllRequests;
+
+/**
* Determines if there is a cache entry for a URL.
*/
- (BOOL)hasDataForURL:(NSString*)url;
@@ -86,7 +132,9 @@
*
* @return nil if hthe URL is not cached or if the cache entry is older than the minimum.
*/
-- (NSData*)getDataForURL:(NSString*)url minTime:(NSTimeInterval)minTime
+- (NSData*)getDataForURL:(NSString*)url expires:(NSTimeInterval)expirationAge
+ timestamp:(NSDate**)timestamp;
+- (NSData*)getDataForKey:(NSString*)key expires:(NSTimeInterval)expirationAge
timestamp:(NSDate**)timestamp;
/**
@@ -133,6 +181,11 @@
*/
- (void)removeURL:(NSString*)url fromDisk:(BOOL)fromDisk;
+/**
+ *
+ */
+- (void)removeKey:(NSString*)key;
+
/**
* Erases the memory cache and optionally the disk cache.
*/
@@ -148,6 +201,11 @@
- (void)invalidateURL:(NSString*)url;
/**
+ *
+ */
+- (void)invalidateKey:(NSString*)key;
+
+/**
* Invalidates all files in the disk cache according to rules explained in `invalidateURL`.
*/
- (void)invalidateAll;
View
67 src/Three20/T3URLRequest.h
@@ -1,19 +1,57 @@
#import "Three20/T3Global.h"
-@protocol T3URLRequestDelegate;
+@protocol T3URLRequestDelegate, T3URLResponseHandler;
@class T3URLCache;
@interface T3URLRequest : NSObject {
- NSString* url;
- id<T3URLRequestDelegate> delegate;
- NSTimeInterval minTime;
- BOOL convertMedia;
+ NSString* _url;
+ NSString* _httpMethod;
+ NSData* _httpBody;
+ NSString* _contentType;
+ id<T3URLRequestDelegate> _delegate;
+ id<T3URLResponseHandler> _handler;
+ id _handlerDelegate;
+ T3URLRequestCachePolicy _cachePolicy;
+ NSTimeInterval _cacheExpirationAge;
+ NSString* _cacheKey;
+ NSDate* _timestamp;
+ BOOL _loading;
+ BOOL _canBeDelayed;
+ BOOL _shouldHandleCookies;
+ BOOL _shouldConvertToMedia;
+ BOOL _responseFromCache;
}
-@property(nonatomic,readonly) id<T3URLRequestDelegate> delegate;
-@property(nonatomic,readonly) NSString* url;
-@property(nonatomic) NSTimeInterval minTime;
-@property(nonatomic) BOOL convertMedia;
+/**
+ * An object that receives messages about the progress of the request.
+ */
+@property(nonatomic,assign) id<T3URLRequestDelegate> delegate;
+
+/**
+ * An object that handles the response data and may parse and validate it.
+ */
+@property(nonatomic,retain) id<T3URLResponseHandler> handler;
+
+/**
+ * This delegate may be notified of any messages that are specific to the kind of response
+ * that is received. The handler may parse the response into certain objects and then
+ * call methods on the handlerDelegate to receive them.
+ */
+@property(nonatomic,assign) id handlerDelegate;
+
+@property(nonatomic,copy) NSString* url;
+@property(nonatomic,copy) NSString* httpMethod;
+@property(nonatomic,retain) NSData* httpBody;
+@property(nonatomic,copy) NSString* contentType;
+@property(nonatomic) T3URLRequestCachePolicy cachePolicy;
+@property(nonatomic) NSTimeInterval cacheExpirationAge;
+@property(nonatomic,retain) NSString* cacheKey;
+@property(nonatomic,retain) NSDate* timestamp;
+@property(nonatomic) BOOL loading;
+@property(nonatomic) BOOL canBeDelayed;
+@property(nonatomic) BOOL shouldHandleCookies;
+@property(nonatomic) BOOL shouldConvertToMedia;
+@property(nonatomic) BOOL responseFromCache;
+ (T3URLRequest*)requestWithURL:(NSString*)url delegate:(id<T3URLRequestDelegate>)delegate;
@@ -73,3 +111,14 @@
- (void)requestCancelled:(T3URLRequest*)request;
@end
+
+@protocol T3URLResponseHandler <T3URLRequestDelegate>
+
+/**
+ * Processes the data from a successful request and determines if it is valid.
+ *
+ * If the data is not valid, return an error. The data will not be cached if there is an error.
+ */
+- (NSError*)request:(T3URLRequest*)request validateData:(NSData*)data;
+
+@end
View
2  src/Three20/T3YouTubeView.h
@@ -1,7 +1,7 @@
#import "Three20/T3Global.h"
@interface T3YouTubeView : UIWebView {
- NSString* url;
+ NSString* _url;
}
@property(nonatomic,copy) NSString* url;
Please sign in to comment.
Something went wrong with that request. Please try again.