Merge pull request #1898 from walkline/master into 5.x

Handle storeImageDataToDisk error.
This commit is contained in:
Bogdan Poplauschi 2017-10-18 13:04:07 +03:00
commit 324563c7d2
6 changed files with 130 additions and 19 deletions

View File

@ -31,6 +31,7 @@ typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable error);
/**
* SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
@ -78,7 +79,18 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
* @param directory Directory to cache disk images in
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
diskCacheDirectory:(nonnull NSString *)directory;
/**
* Init a new cache store with a specific namespace, directory and file manager
*
* @param ns The namespace to use for this cache store
* @param directory Directory to cache disk images in
* @param fileManager The file manager for storing image, if nil then will be created new one
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
fileManager:(nullable NSFileManager *)fileManager NS_DESIGNATED_INITIALIZER;
#pragma mark - Cache paths
@ -103,7 +115,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock;
/**
* Asynchronously store an image into memory and disk cache at the given key.
@ -116,7 +128,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock;
/**
* Asynchronously store an image into memory and disk cache at the given key.
@ -133,7 +145,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock;
/**
* Synchronously store image NSData into disk cache at the given key.
@ -142,8 +154,12 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
*
* @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL
* @param errorPtr NSError pointer. If error occurs then (*errorPtr) != nil.
*/
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
- (BOOL)storeImageDataToDisk:(nullable NSData *)imageData
forKey:(nullable NSString *)key
error:(NSError * _Nullable * _Nullable)errorPtr;
#pragma mark - Query and Retrieve Ops

View File

@ -81,6 +81,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory {
return [self initWithNamespace:ns diskCacheDirectory:directory fileManager: nil];
}
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
fileManager:(nullable NSFileManager *)fileManager {
if ((self = [super init])) {
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
@ -102,7 +108,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
_fileManager = fileManager ? fileManager : [NSFileManager new];
});
#if SD_UIKIT
@ -184,14 +190,14 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
}
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
}
@ -199,10 +205,10 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock {
if (!image || !key) {
if (completionBlock) {
completionBlock();
completionBlock(nil);
}
return;
}
@ -214,31 +220,34 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (toDisk) {
dispatch_async(self.ioQueue, ^{
NSError * writeError = nil;
@autoreleasepool {
NSData *data = imageData;
if (!data && image) {
// If we do not have any data to detect image format, use PNG format
data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:SDImageFormatPNG];
}
[self storeImageDataToDisk:data forKey:key];
[self storeImageDataToDisk:data forKey:key error:&writeError];
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
completionBlock(writeError);
});
}
});
} else {
if (completionBlock) {
completionBlock();
completionBlock(nil);
}
}
}
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
- (BOOL)storeImageDataToDisk:(nullable NSData *)imageData
forKey:(nullable NSString *)key
error:(NSError * _Nullable * _Nullable)errorPtr {
if (!imageData || !key) {
return;
return NO;
}
[self checkIfQueueIsIOQueue];
@ -252,12 +261,17 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
if (![_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil] && errorPtr) {
*errorPtr = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
return NO;
}
// disable iCloud backup
if (self.config.shouldDisableiCloud) {
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
return YES;
}
#pragma mark - Query and Retrieve Ops

View File

@ -12,6 +12,7 @@
321259EC1F39E3240096FE0E /* TestImageStatic.webp in Resources */ = {isa = PBXBuildFile; fileRef = 321259EB1F39E3240096FE0E /* TestImageStatic.webp */; };
321259EE1F39E4110096FE0E /* TestImageAnimated.webp in Resources */ = {isa = PBXBuildFile; fileRef = 321259ED1F39E4110096FE0E /* TestImageAnimated.webp */; };
32E6F0321F3A1B4700A945E6 /* SDWebImageTestDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 32E6F0311F3A1B4700A945E6 /* SDWebImageTestDecoder.m */; };
37D122881EC48B5E00D98CEB /* SDMockFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D122871EC48B5E00D98CEB /* SDMockFileManager.m */; };
433BBBB51D7EF5C00086B6E9 /* SDWebImageDecoderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */; };
433BBBB71D7EF8200086B6E9 /* TestImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 433BBBB61D7EF8200086B6E9 /* TestImage.gif */; };
433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 433BBBB81D7EF8260086B6E9 /* TestImage.png */; };
@ -38,6 +39,8 @@
321259ED1F39E4110096FE0E /* TestImageAnimated.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageAnimated.webp; sourceTree = "<group>"; };
32E6F0301F3A1B4700A945E6 /* SDWebImageTestDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestDecoder.h; sourceTree = "<group>"; };
32E6F0311F3A1B4700A945E6 /* SDWebImageTestDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestDecoder.m; sourceTree = "<group>"; };
37D122861EC48B5E00D98CEB /* SDMockFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDMockFileManager.h; sourceTree = "<group>"; };
37D122871EC48B5E00D98CEB /* SDMockFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDMockFileManager.m; sourceTree = "<group>"; };
433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDecoderTests.m; sourceTree = "<group>"; };
433BBBB61D7EF8200086B6E9 /* TestImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = TestImage.gif; sourceTree = "<group>"; };
433BBBB81D7EF8260086B6E9 /* TestImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestImage.png; sourceTree = "<group>"; };
@ -129,6 +132,8 @@
433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */,
4369C1D01D97F80F007E863A /* SDWebImagePrefetcherTests.m */,
4369C2731D9804B1007E863A /* SDCategoriesTests.m */,
37D122861EC48B5E00D98CEB /* SDMockFileManager.h */,
37D122871EC48B5E00D98CEB /* SDMockFileManager.m */,
2D7AF05E1F329763000083C2 /* SDTestCase.h */,
2D7AF05F1F329763000083C2 /* SDTestCase.m */,
32E6F0301F3A1B4700A945E6 /* SDWebImageTestDecoder.h */,
@ -229,7 +234,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
85E5D3885A03BFC23B050908 /* [CP] Copy Pods Resources */ = {
@ -280,6 +285,7 @@
files = (
32E6F0321F3A1B4700A945E6 /* SDWebImageTestDecoder.m in Sources */,
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */,
37D122881EC48B5E00D98CEB /* SDMockFileManager.m in Sources */,
4369C2741D9804B1007E863A /* SDCategoriesTests.m in Sources */,
2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */,
4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */,

View File

@ -10,6 +10,7 @@
#import <SDWebImage/SDImageCache.h>
#import <SDWebImage/SDWebImageCodersManager.h>
#import "SDWebImageTestDecoder.h"
#import "SDMockFileManager.h"
NSString *kImageTestKey = @"TestImageKey.jpg";
@ -196,7 +197,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
UIImage *image = [UIImage imageWithContentsOfFile:[self testImagePath]];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
[self.sharedImageCache storeImageDataToDisk:imageData forKey:kImageTestKey];
[self.sharedImageCache storeImageDataToDisk:imageData forKey:kImageTestKey error:nil];
UIImage *storedImageFromMemory = [self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey];
expect(storedImageFromMemory).to.equal(nil);
@ -238,7 +239,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
UIImage *image = [UIImage imageWithContentsOfFile:testImagePath];
NSString *key = @"TestPNGImageEncodedToDataAndRetrieveToJPEG";
[cache storeImage:image imageData:nil forKey:key toDisk:YES completion:^{
[cache storeImage:image imageData:nil forKey:key toDisk:YES completion:^(NSError * _Nullable error) {
[cache clearMemory];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
@ -274,6 +275,32 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
[self waitForExpectationsWithCommonTimeout];
}
- (void)test41StoreImageDataToDiskWithError {
NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]];
NSError * error = nil;
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test"
diskCacheDirectory:@"/"
fileManager:[[SDMockFileManager alloc] initWithError:EACCES]];
[cache storeImageDataToDisk:imageData
forKey:kImageTestKey
error:&error];
XCTAssertEqual(error.code, EACCES);
}
- (void)test42StoreImageDataToDiskWithoutError {
NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]];
NSError * error = nil;
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test"
diskCacheDirectory:@"/"
fileManager:[[SDMockFileManager alloc] initWithError:0]];
[cache storeImageDataToDisk:imageData
forKey:kImageTestKey
error:&error];
XCTAssertNil(error);
}
#pragma mark Helper methods
- (void)clearAllCaches{

View File

@ -0,0 +1,15 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <Foundation/Foundation.h>
@interface SDMockFileManager : NSFileManager
- (id)initWithError:(int)errorNumber;
@end

View File

@ -0,0 +1,33 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDMockFileManager.h"
@interface SDMockFileManager ()
@property (nonatomic, assign) int errorNumber;
@end
@implementation SDMockFileManager
- (id)initWithError:(int)errorNumber {
self = [super init];
if (self) {
_errorNumber = errorNumber;
}
return self;
}
- (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary<NSString *,id> *)attr {
errno = self.errorNumber;
return (self.errorNumber == 0);
}
@end