New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config AFImageDownloader NSURLCache and ask AFImageRequestCache implementer if an image should be cached #4010

Merged
merged 9 commits into from Dec 15, 2017
@@ -230,4 +230,12 @@ - (void)testThatPrioritizedImagesWithOldestLastAccessDatesAreRemovedDuringPurge
}
}
#pragma mark - Should Cache Image
- (void)testThatShouldCacheIsYes {
NSURL *url = [NSURL URLWithString:@"http://test.com/image"];
NSString *identifier = @"filter";
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
BOOL result = [self.cache shouldCacheImage:self.testImage forRequest:request withAdditionalIdentifier:identifier];
XCTAssertTrue(result);
}
@end
@@ -22,6 +22,11 @@
#import "AFTestCase.h"
#import "AFImageDownloader.h"
@interface MockAFAutoPurgingImageCache : AFAutoPurgingImageCache
@property (nonatomic, strong) BOOL(^shouldCache)(UIImage*, NSURLRequest*, NSString*);
@property (nonatomic, strong) void(^addCache)(UIImage*, NSString*);
@end
@interface AFImageDownloaderTests : AFTestCase
@property (nonatomic, strong) NSURLRequest *pngRequest;
@property (nonatomic, strong) NSURLRequest *jpegRequest;
@@ -234,6 +239,112 @@ - (void)testThatResponseIsNilWhenReturnedFromCache {
XCTAssertEqual(responseImage1, responseImage2);
}
- (void)testThatImageCacheIsPromptedShouldCache {
XCTestExpectation *expectation3 = [self expectationWithDescription:@"image 1 shouldCache called"];
XCTestExpectation *expectation4 = [self expectationWithDescription:@"image 1 addCache called"];
MockAFAutoPurgingImageCache *mock = [[MockAFAutoPurgingImageCache alloc] init];
mock.shouldCache = ^BOOL(UIImage *img, NSURLRequest *req, NSString *iden) {
[expectation3 fulfill];
return YES;
};
mock.addCache = ^(UIImage *img, NSString *ident) {
[expectation4 fulfill];
};
self.downloader.imageCache = mock;
XCTestExpectation *expectation1 = [self expectationWithDescription:@"image 1 download should succeed"];
__block NSHTTPURLResponse *urlResponse1 = nil;
__block UIImage *responseImage1 = nil;
[self.downloader
downloadImageForURLRequest:self.pngRequest
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
urlResponse1 = response;
responseImage1 = responseObject;
[expectation1 fulfill];
}
failure:nil];
[self waitForExpectationsWithCommonTimeout];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"image 2 download should succeed"];
__block NSHTTPURLResponse *urlResponse2 = nil;
__block UIImage *responseImage2 = nil;
[self.downloader
downloadImageForURLRequest:self.pngRequest
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
urlResponse2 = response;
responseImage2 = responseObject;
[expectation2 fulfill];
}
failure:nil];
[self waitForExpectationsWithCommonTimeout];
XCTAssertNotNil(urlResponse1);
XCTAssertNotNil(responseImage1);
XCTAssertNil(urlResponse2);
XCTAssertEqual(responseImage1, responseImage2);
}
- (void)testThatImageCacheIsPromptedShouldCacheNot {
XCTestExpectation *expectation3 = [self expectationWithDescription:@"image 1 shouldCache called"];
MockAFAutoPurgingImageCache *mock = [[MockAFAutoPurgingImageCache alloc] init];
mock.shouldCache = ^BOOL(UIImage *img, NSURLRequest *req, NSString *iden) {
[expectation3 fulfill];
return NO;
};
mock.addCache = ^(UIImage *img, NSString *ident) {
XCTFail(@"Not expected");
};
self.downloader.imageCache = mock;
XCTestExpectation *expectation1 = [self expectationWithDescription:@"image 1 download should succeed"];
__block NSHTTPURLResponse *urlResponse1 = nil;
__block UIImage *responseImage1 = nil;
[self.downloader
downloadImageForURLRequest:self.pngRequest
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
urlResponse1 = response;
responseImage1 = responseObject;
[expectation1 fulfill];
}
failure:nil];
[self waitForExpectationsWithCommonTimeout];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"image 2 download should succeed"];
__block NSHTTPURLResponse *urlResponse2 = nil;
__block UIImage *responseImage2 = nil;
XCTestExpectation *expectation5 = [self expectationWithDescription:@"image 2 shouldCache called"];
mock.shouldCache = ^BOOL(UIImage *img, NSURLRequest *req, NSString *iden) {
[expectation5 fulfill];
return NO;
};
[self.downloader
downloadImageForURLRequest:self.pngRequest
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
urlResponse2 = response;
responseImage2 = responseObject;
[expectation2 fulfill];
}
failure:nil];
[self waitForExpectationsWithCommonTimeout];
XCTAssertNotNil(urlResponse1);
XCTAssertNotNil(responseImage1);
XCTAssertNotNil(urlResponse2);
XCTAssertNotEqual(responseImage1, responseImage2);
}
- (void)testThatImageDownloadReceiptIsNilForCachedImage {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"image 1 download should succeed"];
AFImageDownloadReceipt *receipt1;
@@ -445,3 +556,24 @@ - (void)testThatReceiptIDMatchesReturnedID {
}
@end
#pragma mark -
@implementation MockAFAutoPurgingImageCache
-(BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
if (self.shouldCache) {
return self.shouldCache(image, request, identifier);
}
else {
return [super shouldCacheImage:image forRequest:request withAdditionalIdentifier:identifier];
}
}
-(void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier{
[super addImage:image withIdentifier:identifier];
if (self.addCache) {
self.addCache(image, identifier);
}
}
@end
@@ -72,6 +72,17 @@ NS_ASSUME_NONNULL_BEGIN
*/
@protocol AFImageRequestCache <AFImageCache>
/**
Asks if the image should be cached using an identifier created from the request and additional identifier.
@param image The image to be cached.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return A BOOL indicating whether or not the image should be added to the cache. YES will cache, NO will prevent caching.
*/
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Adds the image to the cache using an identifier created from the request and additional identifier.
@@ -196,6 +196,10 @@ - (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditional
return key;
}
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier {
return YES;
}
@end
#endif
@@ -81,13 +81,27 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
*/
+ (NSURLCache *)defaultURLCache;
/**
The default `NSURLSessionConfiguration` with common usage parameter values.
*/
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration;
/**
Default initializer
@return An instance of `AFImageDownloader` initialized with default values.
*/
- (instancetype)init;
/**
Initializer with specific `URLSessionConfiguration`
@param configuration The `NSURLSessionConfiguration` to be be used
@return An instance of `AFImageDownloader` initialized with default values and custom `NSURLSessionConfiguration`
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
/**
Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
@@ -106,10 +106,10 @@ @interface AFImageDownloader ()
@end
@implementation AFImageDownloader
+ (NSURLCache *)defaultURLCache {
// It's been discovered that a crash will occur on certain versions
// of iOS if you customize the cache.
//
@@ -143,7 +143,11 @@ + (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
- (instancetype)init {
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration];
return [self initWithSessionConfiguration:defaultConfiguration];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
return [self initWithSessionManager:sessionManager
@@ -250,7 +254,7 @@ - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(self.responseQueue, ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
AFImageDownloaderMergedTask *mergedTask = strongSelf.mergedTasks[URLIdentifier];
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
if (error) {
@@ -262,7 +266,9 @@ - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)
}
}
} else {
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
}
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.successBlock) {
ProTip! Use n and p to navigate between commits in a pull request.