Merge pull request #1681 from rs/testing

Unit Testing + some refactoring due to testing
This commit is contained in:
Bogdan Poplauschi 2016-09-27 15:18:17 +03:00 committed by GitHub
commit 375f29d745
44 changed files with 1623 additions and 965 deletions

View File

@ -46,3 +46,6 @@ script:
- pod install --project-directory=Tests
- xcodebuild clean -workspace SDWebImage.xcworkspace -scheme 'SDWebImage iOS static' -sdk iphonesimulator PLATFORM_NAME=iphonesimulator -configuration Debug | xcpretty -c
- xcodebuild test -workspace SDWebImage.xcworkspace -scheme 'Tests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -configuration Debug | xcpretty -c
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -11,6 +11,7 @@
#import "DetailViewController.h"
#import <SDWebImage/FLAnimatedImageView.h>
#import <SDWebImage/FLAnimatedImageView+WebCache.h>
#import <SDWebImage/UIView+WebCache.h>
@interface MyCustomTableViewCell : UITableViewCell
@ -71,6 +72,7 @@
@"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif",
@"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp",
@"http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp",
@"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png",
nil];
for (int i=0; i<100; i++) {
@ -86,7 +88,7 @@
- (void)flushCache
{
[SDWebImageManager.sharedManager.imageCache clearMemory];
[SDWebImageManager.sharedManager.imageCache clearDisk];
[SDWebImageManager.sharedManager.imageCache clearDiskOnCompletion:nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

View File

@ -10,6 +10,7 @@
[![Dependency Status](https://www.versioneye.com/objective-c/sdwebimage/3.3/badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage/3.3)
[![Reference Status](https://www.versioneye.com/objective-c/sdwebimage/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage/references)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/rs/SDWebImage)
[![codecov](https://codecov.io/gh/rs/SDWebImage/branch/testing/graph/badge.svg)](https://codecov.io/gh/rs/SDWebImage)
This library provides an async image downloader with cache support. For convenience, we added categories for some `UIControl` elements like `UIImageView`.

View File

@ -297,6 +297,18 @@
431BB6F91D06D2C1006A3455 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; settings = {ATTRIBUTES = (Public, ); }; };
431BB6FA1D06D2C1006A3455 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
431BB6FC1D06D2C1006A3455 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C2781D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C2791D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C27A1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C27B1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C27C1D9807EC007E863A /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
4369C27E1D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
4369C27F1D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
4369C2801D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
4369C2811D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
4369C2821D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
4369C2831D9807EC007E863A /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2761D9807EC007E863A /* UIView+WebCache.m */; };
438096721CDFC08200DC626B /* MKAnnotationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
438096731CDFC08F00DC626B /* MKAnnotationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */; };
438096741CDFC09C00DC626B /* UIImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB911762547C00698166 /* UIImage+WebP.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -435,6 +447,18 @@
43A62A641D0E0A8F0089D7DD /* vp8li.h in Headers */ = {isa = PBXBuildFile; fileRef = DA577D681998E6B2007367ED /* vp8li.h */; };
43A62A651D0E0A8F0089D7DD /* webp.c in Sources */ = {isa = PBXBuildFile; fileRef = DA577D691998E6B2007367ED /* webp.c */; };
43A62A661D0E0A8F0089D7DD /* webpi.h in Headers */ = {isa = PBXBuildFile; fileRef = DA577D6A1998E6B2007367ED /* webpi.h */; };
43A918641D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A918651D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A918661D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A918671D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A918681D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A918691D8308FE00B3925F /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A918621D8308FE00B3925F /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; };
43A9186B1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43A9186C1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43A9186D1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43A9186E1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43A9186F1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43A918701D8308FE00B3925F /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A918631D8308FE00B3925F /* SDImageCacheConfig.m */; };
43CE75761CFE9427006C64D0 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 43CE75491CFE9427006C64D0 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
43CE75771CFE9427006C64D0 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 43CE75491CFE9427006C64D0 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
43CE75781CFE9427006C64D0 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 43CE75491CFE9427006C64D0 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -879,9 +903,13 @@
00733A4C1BC487C000A5A117 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4314D1991D0E0E3B004B36C9 /* libSDWebImage watchOS static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImage watchOS static.a"; sourceTree = BUILT_PRODUCTS_DIR; };
431BB7031D06D2C1006A3455 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4369C2751D9807EC007E863A /* UIView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCache.h"; path = "SDWebImage/UIView+WebCache.h"; sourceTree = "<group>"; };
4369C2761D9807EC007E863A /* UIView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+WebCache.m"; path = "SDWebImage/UIView+WebCache.m"; sourceTree = "<group>"; };
4397D2F21D0DDD8C00BB2784 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4397D2F41D0DE2DF00BB2784 /* NSImage+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+WebCache.h"; sourceTree = "<group>"; };
4397D2F51D0DE2DF00BB2784 /* NSImage+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+WebCache.m"; sourceTree = "<group>"; };
4397D2F41D0DE2DF00BB2784 /* NSImage+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+WebCache.h"; path = "SDWebImage/NSImage+WebCache.h"; sourceTree = "<group>"; };
4397D2F51D0DE2DF00BB2784 /* NSImage+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSImage+WebCache.m"; path = "SDWebImage/NSImage+WebCache.m"; sourceTree = "<group>"; };
43A918621D8308FE00B3925F /* SDImageCacheConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDImageCacheConfig.h; sourceTree = "<group>"; };
43A918631D8308FE00B3925F /* SDImageCacheConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDImageCacheConfig.m; sourceTree = "<group>"; };
43CE75491CFE9427006C64D0 /* FLAnimatedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImage.h; sourceTree = "<group>"; };
43CE754A1CFE9427006C64D0 /* FLAnimatedImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImage.m; sourceTree = "<group>"; };
43CE754B1CFE9427006C64D0 /* FLAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImageView.h; sourceTree = "<group>"; };
@ -985,8 +1013,8 @@
A18A6CC6172DC28500419892 /* UIImage+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+GIF.m"; sourceTree = "<group>"; };
AB615301192DA24600A2D8E9 /* UIView+WebCacheOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+WebCacheOperation.h"; sourceTree = "<group>"; };
AB615302192DA24600A2D8E9 /* UIView+WebCacheOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+WebCacheOperation.m"; sourceTree = "<group>"; };
ABBE71A518C43B4D00B75E91 /* UIImageView+HighlightedWebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+HighlightedWebCache.h"; sourceTree = "<group>"; };
ABBE71A618C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+HighlightedWebCache.m"; sourceTree = "<group>"; };
ABBE71A518C43B4D00B75E91 /* UIImageView+HighlightedWebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+HighlightedWebCache.h"; path = "SDWebImage/UIImageView+HighlightedWebCache.h"; sourceTree = "<group>"; };
ABBE71A618C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+HighlightedWebCache.m"; path = "SDWebImage/UIImageView+HighlightedWebCache.m"; sourceTree = "<group>"; };
DA577CA81998E60B007367ED /* bit_reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bit_reader.c; sourceTree = "<group>"; };
DA577CA91998E60B007367ED /* bit_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_reader.h; sourceTree = "<group>"; };
DA577CAA1998E60B007367ED /* bit_reader_inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_reader_inl.h; sourceTree = "<group>"; };
@ -1089,6 +1117,26 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4369C2851D9811BB007E863A /* WebCache Categories */ = {
isa = PBXGroup;
children = (
4397D2F41D0DE2DF00BB2784 /* NSImage+WebCache.h */,
4397D2F51D0DE2DF00BB2784 /* NSImage+WebCache.m */,
535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */,
535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */,
53922D93148C56230056699D /* UIButton+WebCache.h */,
53922D94148C56230056699D /* UIButton+WebCache.m */,
ABBE71A518C43B4D00B75E91 /* UIImageView+HighlightedWebCache.h */,
ABBE71A618C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m */,
53922D95148C56230056699D /* UIImageView+WebCache.h */,
53922D96148C56230056699D /* UIImageView+WebCache.m */,
4369C2751D9807EC007E863A /* UIView+WebCache.h */,
4369C2761D9807EC007E863A /* UIView+WebCache.m */,
);
name = "WebCache Categories";
path = ..;
sourceTree = "<group>";
};
43CE75451CFE9427006C64D0 /* FLAnimatedImage */ = {
isa = PBXGroup;
children = (
@ -1181,6 +1229,7 @@
53922DAA148C56470056699D /* Cache */,
53922DAC148C56DD0056699D /* Utils */,
53922DA9148C562D0056699D /* Categories */,
4369C2851D9811BB007E863A /* WebCache Categories */,
43CE75CD1CFE98B3006C64D0 /* FLAnimatedImage */,
);
path = SDWebImage;
@ -1189,26 +1238,16 @@
53922DA9148C562D0056699D /* Categories */ = {
isa = PBXGroup;
children = (
535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */,
535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */,
5D5B9140188EE8DD006D06BD /* NSData+ImageContentType.h */,
5D5B9141188EE8DD006D06BD /* NSData+ImageContentType.m */,
53922D93148C56230056699D /* UIButton+WebCache.h */,
53922D94148C56230056699D /* UIButton+WebCache.m */,
A18A6CC5172DC28500419892 /* UIImage+GIF.h */,
A18A6CC6172DC28500419892 /* UIImage+GIF.m */,
53EDFB8817623F7C00698166 /* UIImage+MultiFormat.h */,
53EDFB8917623F7C00698166 /* UIImage+MultiFormat.m */,
53EDFB911762547C00698166 /* UIImage+WebP.h */,
53EDFB921762547C00698166 /* UIImage+WebP.m */,
ABBE71A518C43B4D00B75E91 /* UIImageView+HighlightedWebCache.h */,
ABBE71A618C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m */,
53922D95148C56230056699D /* UIImageView+WebCache.h */,
53922D96148C56230056699D /* UIImageView+WebCache.m */,
AB615301192DA24600A2D8E9 /* UIView+WebCacheOperation.h */,
AB615302192DA24600A2D8E9 /* UIView+WebCacheOperation.m */,
4397D2F41D0DE2DF00BB2784 /* NSImage+WebCache.h */,
4397D2F51D0DE2DF00BB2784 /* NSImage+WebCache.m */,
);
name = Categories;
sourceTree = "<group>";
@ -1218,6 +1257,8 @@
children = (
53922D85148C56230056699D /* SDImageCache.h */,
53922D86148C56230056699D /* SDImageCache.m */,
43A918621D8308FE00B3925F /* SDImageCacheConfig.h */,
43A918631D8308FE00B3925F /* SDImageCacheConfig.m */,
);
name = Cache;
sourceTree = "<group>";
@ -1413,10 +1454,12 @@
431739561CDFC8B70008FEB9 /* demux.h in Headers */,
4317394D1CDFC8B20008FEB9 /* utils.h in Headers */,
4397D2F81D0DF44200BB2784 /* MKAnnotationView+WebCache.h in Headers */,
4369C27A1D9807EC007E863A /* UIView+WebCache.h in Headers */,
431739451CDFC8B20008FEB9 /* quant_levels_dec.h in Headers */,
431739361CDFC8B20008FEB9 /* bit_reader_inl.h in Headers */,
4317393A1CDFC8B20008FEB9 /* color_cache.h in Headers */,
431738E11CDFC8A40008FEB9 /* webpi.h in Headers */,
43A918671D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
431739571CDFC8B70008FEB9 /* encode.h in Headers */,
431739351CDFC8B20008FEB9 /* bit_reader.h in Headers */,
43DA7D5D1D1086600028BE58 /* mips_macro.h in Headers */,
@ -1488,6 +1531,7 @@
43DA7CFE1D10865E0028BE58 /* yuv.h in Headers */,
4314D1711D0E0E3B004B36C9 /* huffman.h in Headers */,
4314D1721D0E0E3B004B36C9 /* SDWebImageCompat.h in Headers */,
43A918651D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
4314D1731D0E0E3B004B36C9 /* vp8li.h in Headers */,
4314D1741D0E0E3B004B36C9 /* types.h in Headers */,
4314D1761D0E0E3B004B36C9 /* decode.h in Headers */,
@ -1497,6 +1541,7 @@
4314D17B1D0E0E3B004B36C9 /* huffman_encode.h in Headers */,
43DA7CD91D10865E0028BE58 /* dsp.h in Headers */,
4314D17C1D0E0E3B004B36C9 /* UIImage+WebP.h in Headers */,
4369C2781D9807EC007E863A /* UIView+WebCache.h in Headers */,
4314D17D1D0E0E3B004B36C9 /* SDWebImagePrefetcher.h in Headers */,
4314D17E1D0E0E3B004B36C9 /* bit_writer.h in Headers */,
43DA7CF01D10865E0028BE58 /* neon.h in Headers */,
@ -1558,6 +1603,7 @@
43DA7D931D1086600028BE58 /* lossless.h in Headers */,
431BB6EE1D06D2C1006A3455 /* NSData+ImageContentType.h in Headers */,
431BB6EF1D06D2C1006A3455 /* SDWebImagePrefetcher.h in Headers */,
4369C27B1D9807EC007E863A /* UIView+WebCache.h in Headers */,
431BB6F01D06D2C1006A3455 /* SDWebImageOperation.h in Headers */,
43A62A201D0E0A800089D7DD /* mux_types.h in Headers */,
43A62A2F1D0E0A860089D7DD /* huffman_encode.h in Headers */,
@ -1575,6 +1621,7 @@
431BB6F91D06D2C1006A3455 /* UIImage+GIF.h in Headers */,
431BB6FA1D06D2C1006A3455 /* SDWebImageDownloader.h in Headers */,
431BB6FC1D06D2C1006A3455 /* SDWebImageDecoder.h in Headers */,
43A918681D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1593,6 +1640,7 @@
4397D2C31D0DDD8C00BB2784 /* SDWebImageManager.h in Headers */,
4397D2C41D0DDD8C00BB2784 /* SDImageCache.h in Headers */,
4397D2C51D0DDD8C00BB2784 /* UIImageView+WebCache.h in Headers */,
4369C27C1D9807EC007E863A /* UIView+WebCache.h in Headers */,
4397D2C61D0DDD8C00BB2784 /* random.h in Headers */,
4397D2C71D0DDD8C00BB2784 /* rescaler.h in Headers */,
43DA7DE11D10867D0028BE58 /* extras.h in Headers */,
@ -1606,6 +1654,7 @@
4397D2D11D0DDD8C00BB2784 /* decode.h in Headers */,
4397D2D21D0DDD8C00BB2784 /* webpi.h in Headers */,
43DA7DCB1D1086610028BE58 /* mips_macro.h in Headers */,
43A918691D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
4397D2D31D0DDD8C00BB2784 /* thread.h in Headers */,
4397D2D41D0DDD8C00BB2784 /* quant_levels.h in Headers */,
4397D2D51D0DDD8C00BB2784 /* bit_reader_inl.h in Headers */,
@ -1646,10 +1695,12 @@
431739211CDFC8B20008FEB9 /* endian_inl.h in Headers */,
43CE757D1CFE9427006C64D0 /* FLAnimatedImageView.h in Headers */,
431739541CDFC8B70008FEB9 /* types.h in Headers */,
4369C2791D9807EC007E863A /* UIView+WebCache.h in Headers */,
431738C51CDFC8A30008FEB9 /* alphai.h in Headers */,
4A2CAE041AB4BB5400B6BC39 /* SDWebImage.h in Headers */,
431739511CDFC8B70008FEB9 /* format_constants.h in Headers */,
431739231CDFC8B20008FEB9 /* filters.h in Headers */,
43A918661D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
4A2CAE211AB4BB7000B6BC39 /* SDWebImageManager.h in Headers */,
4A2CAE1F1AB4BB6C00B6BC39 /* SDImageCache.h in Headers */,
43DA7D261D10865F0028BE58 /* mips_macro.h in Headers */,
@ -1728,6 +1779,7 @@
431738BD1CDFC2660008FEB9 /* decode.h in Headers */,
53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */,
5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */,
4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */,
5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */,
431738B01CDFC2630008FEB9 /* huffman_encode.h in Headers */,
438096741CDFC09C00DC626B /* UIImage+WebP.h in Headers */,
@ -1755,6 +1807,7 @@
53EDFB8A17623F7C00698166 /* UIImage+MultiFormat.h in Headers */,
4317387B1CDFC2580008FEB9 /* decode_vp8.h in Headers */,
431738B61CDFC2630008FEB9 /* random.h in Headers */,
43A918641D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2017,6 +2070,7 @@
43DA7D361D1086600028BE58 /* alpha_processing_sse41.c in Sources */,
43DA7D641D1086600028BE58 /* upsampling_mips_dsp_r2.c in Sources */,
431739461CDFC8B20008FEB9 /* random.c in Sources */,
43A9186E1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
43DA7D3D1D1086600028BE58 /* cost_sse2.c in Sources */,
431738DB1CDFC8A40008FEB9 /* tree.c in Sources */,
00733A581BC4880000A5A117 /* SDWebImageManager.m in Sources */,
@ -2060,6 +2114,7 @@
43DA7D681D1086600028BE58 /* yuv_mips_dsp_r2.c in Sources */,
43DA7D651D1086600028BE58 /* upsampling_neon.c in Sources */,
43DA7D371D1086600028BE58 /* alpha_processing.c in Sources */,
4369C2811D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7D691D1086600028BE58 /* yuv_mips32.c in Sources */,
43DA7D5F1D1086600028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
00733A5E1BC4880000A5A117 /* UIImage+MultiFormat.m in Sources */,
@ -2091,6 +2146,7 @@
43DA7CDB1D10865E0028BE58 /* enc_mips_dsp_r2.c in Sources */,
4314D12F1D0E0E3B004B36C9 /* thread.c in Sources */,
4314D1311D0E0E3B004B36C9 /* SDWebImageDownloader.m in Sources */,
4369C27F1D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7CC91D10865E0028BE58 /* alpha_processing.c in Sources */,
4314D1321D0E0E3B004B36C9 /* filters.c in Sources */,
4314D1341D0E0E3B004B36C9 /* UIImage+WebP.m in Sources */,
@ -2119,6 +2175,7 @@
43DA7CFD1D10865E0028BE58 /* yuv.c in Sources */,
4314D1401D0E0E3B004B36C9 /* UIImageView+WebCache.m in Sources */,
43DA7CDC1D10865E0028BE58 /* enc_mips32.c in Sources */,
43A9186C1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
4314D1411D0E0E3B004B36C9 /* SDWebImageDownloaderOperation.m in Sources */,
43DA7CCE1D10865E0028BE58 /* cost_mips32.c in Sources */,
4314D1421D0E0E3B004B36C9 /* webp.c in Sources */,
@ -2187,6 +2244,7 @@
43DA7D801D1086600028BE58 /* enc_mips_dsp_r2.c in Sources */,
43A62A631D0E0A8F0089D7DD /* vp8l.c in Sources */,
431BB6A31D06D2C1006A3455 /* UIImageView+WebCache.m in Sources */,
4369C2821D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7D6E1D1086600028BE58 /* alpha_processing.c in Sources */,
43A62A5E1D0E0A8F0089D7DD /* io.c in Sources */,
43A62A321D0E0A860089D7DD /* quant_levels_dec.c in Sources */,
@ -2215,6 +2273,7 @@
43DA7DA21D1086600028BE58 /* yuv.c in Sources */,
431BB6B91D06D2C1006A3455 /* UIButton+WebCache.m in Sources */,
43DA7D811D1086600028BE58 /* enc_mips32.c in Sources */,
43A9186F1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
43A62A5F1D0E0A8F0089D7DD /* quant.c in Sources */,
43DA7D731D1086600028BE58 /* cost_mips32.c in Sources */,
431BB6BD1D06D2C1006A3455 /* UIImage+GIF.m in Sources */,
@ -2335,11 +2394,13 @@
4397D2AA1D0DDD8C00BB2784 /* io.c in Sources */,
43DA7DCF1D1086610028BE58 /* rescaler_neon.c in Sources */,
43DA7DC31D1086610028BE58 /* lossless_enc_sse2.c in Sources */,
43A918701D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
4397D2AB1D0DDD8C00BB2784 /* UIView+WebCacheOperation.m in Sources */,
43DA7DA91D1086610028BE58 /* cost_mips_dsp_r2.c in Sources */,
43DA7DB01D1086610028BE58 /* dec_mips32.c in Sources */,
43DA7DAA1D1086610028BE58 /* cost_mips32.c in Sources */,
43DA7DAB1D1086610028BE58 /* cost_sse2.c in Sources */,
4369C2831D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7DB71D1086610028BE58 /* enc_mips_dsp_r2.c in Sources */,
43DA7DC11D1086610028BE58 /* lossless_enc_mips32.c in Sources */,
43DA7DBC1D1086610028BE58 /* enc.c in Sources */,
@ -2407,6 +2468,7 @@
43DA7CFF1D10865F0028BE58 /* alpha_processing_sse41.c in Sources */,
43DA7D2D1D10865F0028BE58 /* upsampling_mips_dsp_r2.c in Sources */,
4A2CAE261AB4BB7000B6BC39 /* SDWebImagePrefetcher.m in Sources */,
43A9186D1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
43DA7D061D10865F0028BE58 /* cost_sse2.c in Sources */,
4317391F1CDFC8B20008FEB9 /* color_cache.c in Sources */,
4A2CAE301AB4BB7500B6BC39 /* UIImage+MultiFormat.m in Sources */,
@ -2450,6 +2512,7 @@
43DA7D311D10865F0028BE58 /* yuv_mips_dsp_r2.c in Sources */,
43DA7D2E1D10865F0028BE58 /* upsampling_neon.c in Sources */,
43DA7D001D10865F0028BE58 /* alpha_processing.c in Sources */,
4369C2801D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7D321D10865F0028BE58 /* yuv_mips32.c in Sources */,
43DA7D281D10865F0028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
431738C81CDFC8A30008FEB9 /* frame.c in Sources */,
@ -2507,6 +2570,7 @@
43DA7C911D1086570028BE58 /* alpha_processing_sse41.c in Sources */,
43DA7CBF1D1086570028BE58 /* upsampling_mips_dsp_r2.c in Sources */,
5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */,
43A9186B1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
43DA7C981D1086570028BE58 /* cost_sse2.c in Sources */,
431738A81CDFC2630008FEB9 /* color_cache.c in Sources */,
5376130E155AD0D5005750A4 /* UIButton+WebCache.m in Sources */,
@ -2550,6 +2614,7 @@
43DA7CC31D1086570028BE58 /* yuv_mips_dsp_r2.c in Sources */,
43DA7CC01D1086570028BE58 /* upsampling_neon.c in Sources */,
43DA7C921D1086570028BE58 /* alpha_processing.c in Sources */,
4369C27E1D9807EC007E863A /* UIView+WebCache.m in Sources */,
43DA7CC41D1086570028BE58 /* yuv_mips32.c in Sources */,
43DA7CBA1D1086570028BE58 /* rescaler_mips_dsp_r2.c in Sources */,
4317387C1CDFC2580008FEB9 /* frame.c in Sources */,
@ -2847,7 +2912,6 @@
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"SD_WEBP=1",
"DEBUG=1",
"$(inherited)",
);
@ -2901,10 +2965,7 @@
);
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"SD_WEBP=1",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;

View File

@ -11,9 +11,9 @@
#if SD_UIKIT
#if COCOAPODS
@import FLAnimatedImage;
@import FLAnimatedImage;
#else
#import "FLAnimatedImageView.h"
#import "FLAnimatedImageView.h"
#endif
#import "SDWebImageManager.h"
@ -25,14 +25,6 @@
*/
@interface FLAnimatedImageView (WebCache)
/**
* Get the current image URL.
*
* Note that because of the limitations of categories this property can get out of sync
* if you use setImage: directly.
*/
- (nullable NSURL *)sd_imageURL;
/**
* Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
* The download is asynchronous and cached.
@ -136,11 +128,6 @@
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
/**
* Cancel the image load
*/
- (void)sd_cancelCurrentImageLoad;
@end
#endif

View File

@ -11,19 +11,13 @@
#import "FLAnimatedImageView+WebCache.h"
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"
#import "UIView+WebCache.h"
#import "NSData+ImageContentType.h"
#import "FLAnimatedImage.h"
#import "UIImageView+WebCache.h"
static char imageURLKey;
@implementation FLAnimatedImageView (WebCache)
- (nullable NSURL *)sd_imageURL {
return objc_getAssociatedObject(self, &imageURLKey);
}
- (void)sd_setImageWithURL:(nullable NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}
@ -53,70 +47,25 @@ static char imageURLKey;
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_cancelCurrentImageLoad];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
if (url) {
// check if activityView is enabled or not
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
[wself removeActivityIndicator];
if (!wself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
NSString *imageContentType = [NSData sd_contentTypeForImageData:data];
if ([imageContentType isEqualToString:@"image/gif"]) {
wself.animatedImage = [FLAnimatedImage animatedImageWithGIFData:data];
wself.image = nil;
} else {
wself.image = image;
wself.animatedImage = nil;
}
[wself setNeedsLayout];
} else {
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
[wself setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
} else {
dispatch_main_async_safe(^{
[self removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:^(UIImage *image, NSData *imageData) {
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
if (imageFormat == SDImageFormatGIF) {
weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:data];
weakSelf.image = nil;
} else {
weakSelf.image = image;
weakSelf.animatedImage = nil;
}
}
progress:progressBlock
completed:completedBlock];
}
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
}
@end
#endif

View File

@ -18,14 +18,6 @@
*/
@interface MKAnnotationView (WebCache)
/**
* Get the current image URL.
*
* Note that because of the limitations of categories this property can get out of sync
* if you use setImage: directly.
*/
- (nullable NSURL *)sd_imageURL;
/**
* Set the imageView `image` with an `url`.
*
@ -112,11 +104,6 @@
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock;
/**
* Cancel the current download
*/
- (void)sd_cancelCurrentImageLoad;
@end
#endif

View File

@ -12,15 +12,10 @@
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"
static char imageURLKey;
#import "UIView+WebCache.h"
@implementation MKAnnotationView (WebCache)
- (nullable NSURL *)sd_imageURL {
return objc_getAssociatedObject(self, &imageURLKey);
}
- (void)sd_setImageWithURL:(nullable NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
}
@ -45,56 +40,16 @@ static char imageURLKey;
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_cancelCurrentImageLoad];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.image = placeholder;
if (url) {
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong MKAnnotationView *sself = wself;
if (!sself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
wself.image = image;
#if SD_UIKIT
[wself setNeedsLayout];
#elif SD_MAC
[wself setNeedsLayout:YES];
#endif
} else {
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
#if SD_UIKIT
[wself setNeedsLayout];
#elif SD_MAC
[wself setNeedsDisplay:YES];
#endif
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"MKAnnotationViewImage"];
} else {
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:@"MKAnnotationViewImage"];
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:^(UIImage *image, NSData *imageData) {
weakSelf.image = image;
}
progress:nil
completed:completedBlock];
}
@end

View File

@ -9,15 +9,24 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, SDImageFormat) {
SDImageFormatUndefined = -1,
SDImageFormatJPEG = 0,
SDImageFormatPNG,
SDImageFormatGIF,
SDImageFormatTIFF,
SDImageFormatWebP
};
@interface NSData (ImageContentType)
/**
* Compute the content type for an image data
* Return image format
*
* @param data the input data
* @param data the input image data
*
* @return the content type as string (i.e. image/jpeg, image/gif)
* @return the image format as `SDImageFormat` (enum)
*/
+ (nullable NSString *)sd_contentTypeForImageData:(nullable NSData *)data;
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;
@end

View File

@ -12,37 +12,35 @@
@implementation NSData (ImageContentType)
+ (nullable NSString *)sd_contentTypeForImageData:(nullable NSData *)data {
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
if (!data) {
return nil;
return SDImageFormatUndefined;
}
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
return SDImageFormatJPEG;
case 0x89:
return @"image/png";
return SDImageFormatPNG;
case 0x47:
return @"image/gif";
return SDImageFormatGIF;
case 0x49:
case 0x4D:
return @"image/tiff";
return SDImageFormatTIFF;
case 0x52:
// R as RIFF for WEBP
if (data.length < 12) {
return nil;
return SDImageFormatUndefined;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
return SDImageFormatWebP;
}
return nil;
}
return nil;
return SDImageFormatUndefined;
}
@end

View File

@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"
@class SDImageCacheConfig;
typedef NS_ENUM(NSInteger, SDImageCacheType) {
/**
* The image wasn't available the SDWebImage caches, but was downloaded from the web.
@ -30,27 +32,19 @@ typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
/**
* SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
* asynchronous so it doesnt add unnecessary latency to the UI.
*/
@interface SDImageCache : NSObject
/**
* Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
#pragma mark - Properties
/**
* disable iCloud backup [defaults to YES]
* Cache Config object - storing all kind of settings
*/
@property (assign, nonatomic) BOOL shouldDisableiCloud;
/**
* use memory cache [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
/**
* The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
@ -62,15 +56,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
*/
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
/**
* The maximum length of time to keep an image in the cache, in seconds
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* The maximum size of the cache, in bytes.
*/
@property (assign, nonatomic) NSUInteger maxCacheSize;
#pragma mark - Singleton and initialization
/**
* Returns global shared cache instance
@ -95,6 +81,8 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
#pragma mark - Cache paths
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
/**
@ -105,126 +93,140 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
*/
- (void)addReadOnlyCachePath:(nonnull NSString *)path;
/**
* Store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
*/
- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key;
#pragma mark - Store Ops
/**
* Store an image into memory and optionally disk cache at the given key.
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param completion A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key toDisk:(BOOL)toDisk;
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/**
* Store an image into memory and optionally disk cache at the given key.
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage
* @param imageData The image data as returned by the server, this representation will be used for disk storage
* instead of converting the given image object into a storable/compressed image format in order
* to save quality and CPU
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param completion A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key toDisk:(BOOL)toDisk;
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/**
* Store image NSData into disk cache at the given key.
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL
* @param image The image to store
* @param imageData The image data as returned by the server, this representation will be used for disk storage
* instead of converting the given image object into a storable/compressed image format in order
* to save quality and CPU
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param completion A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/**
* Synchronously store image NSData into disk cache at the given key.
*
* @warning This method is synchronous, make sure to call it from the ioQueue
*
* @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL
*/
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
#pragma mark - Query and Retrieve Ops
/**
* Query the disk cache asynchronously.
* Async check if image exists in disk cache already (does not load the image)
*
* @param key The unique key used to store the wanted image
* @param key the key describing the url
* @param completionBlock the block to be executed when the check is done.
* @note the completion block will be always executed on the main queue
*/
- (nullable NSOperation *)queryDiskCacheForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
* Operation that queries the cache asynchronously and call the completion when done.
*
* @param key The unique key used to store the wanted image
* @param doneBlock The completion block. Will not get called if the operation is cancelled
*
* @return a NSOperation instance containing the cache op
*/
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
/**
* Query the memory cache synchronously.
*
* @param key The unique key used to store the wanted image
* @param key The unique key used to store the image
*/
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
/**
* Query the disk cache synchronously after checking the memory cache.
* Query the disk cache synchronously.
*
* @param key The unique key used to store the wanted image
* @param key The unique key used to store the image
*/
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
/**
* Remove the image from memory and disk cache asynchronously
* Query the cache (memory and or disk) synchronously after checking the memory cache.
*
* @param key The unique image cache key
* @param key The unique key used to store the image
*/
- (void)removeImageForKey:(nullable NSString *)key;
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
#pragma mark - Remove Ops
/**
* Remove the image from memory and disk cache asynchronously
*
* @param key The unique image cache key
* @param completion An block that should be executed after the image has been removed (optional)
* @param completion A block that should be executed after the image has been removed (optional)
*/
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
/**
* Remove the image from memory and optionally disk cache asynchronously
*
* @param key The unique image cache key
* @param fromDisk Also remove cache entry from disk if YES
*/
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk;
/**
* Remove the image from memory and optionally disk cache asynchronously
*
* @param key The unique image cache key
* @param fromDisk Also remove cache entry from disk if YES
* @param completion An block that should be executed after the image has been removed (optional)
* @param completion A block that should be executed after the image has been removed (optional)
*/
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
#pragma mark - Cache clean Ops
/**
* Clear all memory cached images
*/
- (void)clearMemory;
/**
* Clear all disk cached images. Non-blocking method - returns immediately.
* @param completion An block that should be executed after cache expiration completes (optional)
* Async clear all disk cached images. Non-blocking method - returns immediately.
* @param completion A block that should be executed after cache expiration completes (optional)
*/
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
/**
* Clear all disk cached images
* @see clearDiskOnCompletion:
* Async remove all expired cached image from disk. Non-blocking method - returns immediately.
* @param completionBlock A block that should be executed after cache expiration completes (optional)
*/
- (void)clearDisk;
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
/**
* Remove all expired cached image from disk. Non-blocking method - returns immediately.
* @param completionBlock An block that should be executed after cache expiration completes (optional)
*/
- (void)cleanDiskWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
/**
* Remove all expired cached image from disk
* @see cleanDiskWithCompletionBlock:
*/
- (void)cleanDisk;
#pragma mark - Cache Info
/**
* Get the size used by the disk cache
@ -241,23 +243,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
*/
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
/**
* Async check if image exists in disk cache already (does not load the image)
*
* @param key the key describing the url
* @param completionBlock the block to be executed when the check is done.
* @note the completion block will be always executed on the main queue
*/
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
* Check if image exists in disk cache already (does not load the image)
*
* @param key the key describing the url
*
* @return YES if an image exists for the given key
*/
- (BOOL)diskImageExistsWithKey:(nullable NSString *)key;
#pragma mark - Cache Paths
/**
* Get the cache path for a certain key (needs the cache path root folder)

View File

@ -13,6 +13,7 @@
#import "UIImage+GIF.h"
#import "NSData+ImageContentType.h"
#import "NSImage+WebCache.h"
#import "SDImageCacheConfig.h"
// See https://github.com/rs/SDWebImage/pull/1141 for discussion
@interface AutoPurgeCache : NSCache
@ -38,23 +39,6 @@
@end
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
// PNG signature bytes and data (below)
static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
static NSData *kPNGSignatureData = nil;
BOOL ImageDataHasPNGPreffix(NSData *data);
BOOL ImageDataHasPNGPreffix(NSData *data) {
NSUInteger pngSignatureLength = kPNGSignatureData.length;
if (data.length >= pngSignatureLength) {
if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) {
return YES;
}
}
return NO;
}
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
@ -66,6 +50,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
@interface SDImageCache ()
#pragma mark - Properties
@property (strong, nonatomic, nonnull) NSCache *memCache;
@property (strong, nonatomic, nonnull) NSString *diskCachePath;
@property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths;
@ -78,6 +63,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
NSFileManager *_fileManager;
}
#pragma mark - Singleton, init, dealloc
+ (nonnull SDImageCache *)sharedImageCache {
static dispatch_once_t once;
static id instance;
@ -100,16 +87,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
diskCacheDirectory:(nonnull NSString *)directory {
if ((self = [super init])) {
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// initialise PNG signature data
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
// Create IO serial queue
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
// Init default values
_maxCacheAge = kDefaultCacheMaxCacheAge;
_config = [[SDImageCacheConfig alloc] init];
// Init the memory cache
_memCache = [[AutoPurgeCache alloc] init];
_memCache.name = fullNamespace;
@ -122,15 +105,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
_diskCachePath = path;
}
// Set decompression to YES
_shouldDecompressImages = YES;
// memory cache enabled
_shouldCacheImagesInMemory = YES;
// Disable iCloud
_shouldDisableiCloud = YES;
dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
});
@ -143,12 +117,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
selector:@selector(deleteOldFiles)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
selector:@selector(backgroundDeleteOldFiles)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
@ -162,6 +136,16 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
SDDispatchQueueRelease(_ioQueue);
}
- (void)checkIfQueueIsIOQueue {
const char *currentQueueLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
const char *ioQueueLabel = dispatch_queue_get_label(self.ioQueue);
if (strcmp(currentQueueLabel, ioQueueLabel) != 0) {
NSLog(@"This method should be called from the ioQueue");
}
}
#pragma mark - Cache paths
- (void)addReadOnlyCachePath:(nonnull NSString *)path {
if (!self.customPaths) {
self.customPaths = [NSMutableArray new];
@ -181,8 +165,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return [self cachePathForKey:key inPath:self.diskCachePath];
}
#pragma mark SDImageCache (private)
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
@ -197,88 +179,73 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return filename;
}
#pragma mark ImageCache
// Init the disk cache
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return [paths[0] stringByAppendingPathComponent:fullNamespace];
}
- (void)storeImage:(nullable UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key toDisk:(BOOL)toDisk {
#pragma mark - Store Ops
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)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 {
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
}
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
if (!image || !key) {
if (completionBlock) {
completionBlock();
}
return;
}
// if memory cache is enabled
if (self.shouldCacheImagesInMemory) {
if (self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
}
if (toDisk) {
dispatch_async(self.ioQueue, ^{
NSData *data = imageData;
if (image && (recalculate || !data)) {
#if SD_UIKIT || SD_WATCH
// We need to determine if the image is a PNG or a JPEG
// PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
// The first eight bytes of a PNG file always contain the following (decimal) values:
// 137 80 78 71 13 10 26 10
// If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download)
// and the image has an alpha channel, we will consider it PNG to avoid losing the transparency
int alphaInfo = CGImageGetAlphaInfo(image.CGImage);
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL imageIsPng = hasAlpha;
// But if we have an image data, we will look at the preffix
if (imageData.length >= kPNGSignatureData.length) {
imageIsPng = ImageDataHasPNGPreffix(imageData);
}
if (imageIsPng) {
data = UIImagePNGRepresentation(image);
}
else {
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
}
#else
NSString *contentType = [NSData sd_contentTypeForImageData:data];
NSBitmapImageFileType imageFileType = NSJPEGFileType;
if ([contentType isEqualToString:@"image/gif"]) {
imageFileType = NSGIFFileType;
} else if ([contentType isEqualToString:@"image/png"]) {
imageFileType = NSPNGFileType;
}
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations
usingType:imageFileType
properties:@{}];
#endif
if (!data && image) {
SDImageFormat imageFormatFromData = [NSData sd_imageFormatForImageData:data];
data = [image sd_imageDataAsFormat:imageFormatFromData];
}
[self storeImageDataToDisk:data forKey:key];
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
if (completionBlock) {
completionBlock();
}
}
}
- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key {
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES];
}
- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key toDisk:(BOOL)toDisk {
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk];
}
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
if (!imageData) {
if (!imageData || !key) {
return;
}
[self checkIfQueueIsIOQueue];
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
@ -291,26 +258,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
// disable iCloud backup
if (self.shouldDisableiCloud) {
if (self.config.shouldDisableiCloud) {
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
- (BOOL)diskImageExistsWithKey:(nullable NSString *)key {
BOOL exists = NO;
// this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance
// from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely.
exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]];
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
if (!exists) {
exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];
}
return exists;
}
#pragma mark - Query and Retrieve Ops
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
dispatch_async(_ioQueue, ^{
@ -335,16 +288,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
}
// Second check the disk cache...
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
@ -352,6 +297,18 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return diskImage;
}
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
}
// Second check the disk cache...
image = [self imageFromDiskCacheForKey:key];
return image;
}
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
NSString *defaultPath = [self defaultCachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:defaultPath];
@ -390,7 +347,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (data) {
UIImage *image = [UIImage sd_imageWithData:data];
image = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) {
if (self.config.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image];
}
return image;
@ -404,13 +361,11 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return SDScaledImageForKey(key, image);
}
- (nullable NSOperation *)queryDiskCacheForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!doneBlock) {
return nil;
}
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!key) {
doneBlock(nil, nil, SDImageCacheTypeNone);
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
@ -421,52 +376,50 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if ([image isGIF]) {
diskData = [self diskImageDataBySearchingAllPathsForKey:key];
}
doneBlock(image, diskData, SDImageCacheTypeMemory);
if (doneBlock) {
doneBlock(image, diskData, SDImageCacheTypeMemory);
}
return nil;
}
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
// do not call the completion if cancelled
return;
}
@autoreleasepool {
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
});
if (doneBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
});
}
}
});
return operation;
}
- (void)removeImageForKey:(nullable NSString *)key {
[self removeImageForKey:key withCompletion:nil];
}
#pragma mark - Remove Ops
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion {
[self removeImageForKey:key fromDisk:YES withCompletion:completion];
}
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk {
[self removeImageForKey:key fromDisk:fromDisk withCompletion:nil];
}
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion {
if (key == nil) {
return;
}
if (self.shouldCacheImagesInMemory) {
if (self.config.shouldCacheImagesInMemory) {
[self.memCache removeObjectForKey:key];
}
@ -486,6 +439,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
# pragma mark - Mem Cache settings
- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost {
self.memCache.totalCostLimit = maxMemoryCost;
}
@ -502,14 +457,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
self.memCache.countLimit = maxCountLimit;
}
#pragma mark - Cache clean Ops
- (void)clearMemory {
[self.memCache removeAllObjects];
}
- (void)clearDisk {
[self clearDiskOnCompletion:nil];
}
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion {
dispatch_async(self.ioQueue, ^{
[_fileManager removeItemAtPath:self.diskCachePath error:nil];
@ -526,11 +479,11 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
});
}
- (void)cleanDisk {
[self cleanDiskWithCompletionBlock:nil];
- (void)deleteOldFiles {
[self deleteOldFilesWithCompletionBlock:nil];
}
- (void)cleanDiskWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
@ -541,7 +494,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
@ -577,9 +530,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
@ -609,7 +562,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
#if SD_UIKIT
- (void)backgroundCleanDisk {
- (void)backgroundDeleteOldFiles {
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
@ -623,13 +576,15 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}];
// Start the long-running task and return immediately.
[self cleanDiskWithCompletionBlock:^{
[self deleteOldFilesWithCompletionBlock:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
#endif
#pragma mark - Cache Info
- (NSUInteger)getSize {
__block NSUInteger size = 0;
dispatch_sync(self.ioQueue, ^{
@ -680,3 +635,4 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
@end

View File

@ -0,0 +1,39 @@
//
// SDImageCacheConfig.h
// SDWebImage
//
// Created by Bogdan on 09/09/16.
// Copyright © 2016 Dailymotion. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface SDImageCacheConfig : NSObject
/**
* Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
/**
* disable iCloud backup [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldDisableiCloud;
/**
* use memory cache [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
/**
* The maximum length of time to keep an image in the cache, in seconds
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* The maximum size of the cache, in bytes.
*/
@property (assign, nonatomic) NSUInteger maxCacheSize;
@end

View File

@ -0,0 +1,26 @@
//
// SDImageCacheConfig.m
// SDWebImage
//
// Created by Bogdan on 09/09/16.
// Copyright © 2016 Dailymotion. All rights reserved.
//
#import "SDImageCacheConfig.h"
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
@implementation SDImageCacheConfig
- (instancetype)init {
if (self = [super init]) {
_shouldDecompressImages = YES;
_shouldDisableiCloud = YES;
_shouldCacheImagesInMemory = YES;
_maxCacheAge = kDefaultCacheMaxCacheAge;
_maxCacheSize = 0;
}
return self;
}
@end

View File

@ -112,3 +112,5 @@ extern NSString *const SDWebImageErrorDomain;
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
static int64_t kAsyncTestTimeout = 5;

View File

@ -97,6 +97,9 @@ typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterB
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
/**
* The maximum number of concurrent downloads
*/
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
/**

View File

@ -129,7 +129,11 @@
}
- (void)setOperationClass:(nullable Class)operationClass {
_operationClass = operationClass ?: [SDWebImageDownloaderOperation class];
if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
_operationClass = operationClass;
} else {
_operationClass = [SDWebImageDownloaderOperation class];
}
}
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url

View File

@ -15,7 +15,30 @@ extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
extern NSString * _Nonnull const SDWebImageDownloadFinishNotification;
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
/**
Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol
*/
@protocol SDWebImageDownloaderOperationInterface<NSObject>
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options;
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (BOOL)shouldDecompressImages;
- (void)setShouldDecompressImages:(BOOL)value;
- (nullable NSURLCredential *)credential;
- (void)setCredential:(nullable NSURLCredential *)value;
@end
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageDownloaderOperationInterface, SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
/**
* The request used by the operation's task.

View File

@ -39,7 +39,6 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
@property (strong, atomic, nullable) NSThread *thread;
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
#if SD_UIKIT
@ -164,7 +163,6 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
self.dataTask = [session dataTaskWithRequest:self.request];
self.executing = YES;
self.thread = [NSThread currentThread];
}
[self.dataTask resume];
@ -197,20 +195,10 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
- (void)cancel {
@synchronized (self) {
if (self.thread) {
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
else {
[self cancelInternal];
}
[self cancelInternal];
}
}
- (void)cancelInternalAndStop {
if (self.isFinished) return;
[self cancelInternal];
}
- (void)cancelInternal {
if (self.isFinished) return;
[super cancel];
@ -242,7 +230,6 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
});
self.dataTask = nil;
self.imageData = nil;
self.thread = nil;
if (self.ownedSession) {
[self.ownedSession invalidateAndCancel];
self.ownedSession = nil;
@ -423,7 +410,6 @@ didReceiveResponse:(NSURLResponse *)response
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSArray<id> *completionBlocks = [[self callbacksForKey:kCompletedCallbackKey] copy];
@synchronized(self) {
self.thread = nil;
self.dataTask = nil;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];

View File

@ -236,24 +236,6 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager];
*/
- (BOOL)isRunning;
/**
* Check if image has already been cached
*
* @param url image url
*
* @return if the image was already cached
*/
- (BOOL)cachedImageExistsForURL:(nullable NSURL *)url;
/**
* Check if image has already been cached on disk only
*
* @param url image url
*
* @return if the image was already cached (disk only)
*/
- (BOOL)diskImageExistsForURL:(nullable NSURL *)url;
/**
* Async check if image has already been cached
*

View File

@ -66,17 +66,6 @@
}
}
- (BOOL)cachedImageExistsForURL:(nullable NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES;
return [self.imageCache diskImageExistsWithKey:key];
}
- (BOOL)diskImageExistsForURL:(nullable NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
return [self.imageCache diskImageExistsWithKey:key];
}
- (void)cachedImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
NSString *key = [self cacheKeyForURL:url];
@ -154,7 +143,7 @@
}
NSString *key = [self cacheKeyForURL:url];
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
if (operation.isCancelled) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
@ -229,7 +218,8 @@
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk];
// pass nil if the image was transformed, so we can recalculate the data from the image
[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
}
dispatch_main_sync_safe(^{
@ -240,7 +230,7 @@
});
} else {
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:downloadedData forKey:key toDisk:cacheOnDisk];
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
}
dispatch_main_sync_safe(^{
@ -299,7 +289,7 @@
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url {
if (image && url) {
NSString *key = [self cacheKeyForURL:url];
[self.imageCache storeImage:image forKey:key toDisk:YES];
[self.imageCache storeImage:image forKey:key toDisk:YES completion:nil];
}
}

View File

@ -22,6 +22,8 @@
*/
- (nullable NSURL *)sd_currentImageURL;
#pragma mark - Image
/**
* Get the image URL for a control state.
*
@ -126,6 +128,8 @@
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock;
#pragma mark - Background image
/**
* Set the backgroundImageView `image` with an `url`.
*
@ -222,6 +226,8 @@
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock;
#pragma mark - Cancel
/**
* Cancel the current image download
*/

View File

@ -12,6 +12,7 @@
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"
#import "UIView+WebCache.h"
static char imageURLStorageKey;
@ -33,6 +34,8 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
return self.imageURLStorage[@(state)];
}
#pragma mark - Image
- (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
@ -58,46 +61,27 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self setImage:placeholder forState:state];
[self sd_cancelImageLoadForState:state];
if (!url) {
[self.imageURLStorage removeObjectForKey:@(state)];
dispatch_main_async_safe(^{
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
return;
}
self.imageURLStorage[@(state)] = url;
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong UIButton *sself = wself;
if (!sself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
[sself setImage:image forState:state];
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forState:state];
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]
setImageBlock:^(UIImage *image, NSData *imageData) {
[weakSelf setImage:image forState:state];
}
progress:nil
completed:completedBlock];
}
#pragma mark - Background image
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
@ -123,39 +107,23 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_cancelBackgroundImageLoadForState:state];
[self setBackgroundImage:placeholder forState:state];
if (url) {
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong UIButton *sself = wself;
if (!sself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
[sself setBackgroundImage:image forState:state];
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setBackgroundImageLoadOperation:operation forState:state];
} else {
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
if (!url) {
[self.imageURLStorage removeObjectForKey:@(state)];
return;
}
self.imageURLStorage[@(state)] = url;
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]
setImageBlock:^(UIImage *image, NSData *imageData) {
[weakSelf setBackgroundImage:image forState:state];
}
progress:nil
completed:completedBlock];
}
- (void)sd_setImageLoadOperation:(id<SDWebImageOperation>)operation forState:(UIControlState)state {
@ -176,8 +144,7 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
- (SDStateImageURLDictionary *)imageURLStorage {
SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
if (!storage)
{
if (!storage) {
storage = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

View File

@ -7,9 +7,12 @@
*/
#import "SDWebImageCompat.h"
#import "NSData+ImageContentType.h"
@interface UIImage (MultiFormat)
+ (nullable UIImage *)sd_imageWithData:(nullable NSData *)data;
- (nullable NSData *)sd_imageData;
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat;
@end

View File

@ -23,12 +23,12 @@
}
UIImage *image;
NSString *imageContentType = [NSData sd_contentTypeForImageData:data];
if ([imageContentType isEqualToString:@"image/gif"]) {
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
if (imageFormat == SDImageFormatGIF) {
image = [UIImage sd_animatedGIFWithData:data];
}
#ifdef SD_WEBP
else if ([imageContentType isEqualToString:@"image/webp"])
else if (imageFormat == SDImageFormatWebP)
{
image = [UIImage sd_imageWithWebPData:data];
}
@ -116,5 +116,46 @@
}
#endif
- (nullable NSData *)sd_imageData {
return [self sd_imageDataAsFormat:SDImageFormatUndefined];
}
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {
NSData *imageData = nil;
if (self) {
#if SD_UIKIT || SD_WATCH
int alphaInfo = CGImageGetAlphaInfo(self.CGImage);
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL usePNG = hasAlpha;
// the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel
if (imageFormat != SDImageFormatUndefined) {
usePNG = (imageFormat == SDImageFormatPNG);
}
if (usePNG) {
imageData = UIImagePNGRepresentation(self);
} else {
imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);
}
#else
NSBitmapImageFileType imageFileType = NSJPEGFileType;
if (imageFormat == SDImageFormatGIF) {
imageFileType = NSGIFFileType;
} else if (imageFormat == SDImageFormatPNG) {
imageFileType = NSPNGFileType;
}
imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations
usingType:imageFileType
properties:@{}];
#endif
}
return imageData;
}
@end

View File

@ -88,11 +88,6 @@
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
/**
* Cancel the current download
*/
- (void)sd_cancelCurrentHighlightedImageLoad;
@end
#endif

View File

@ -11,8 +11,7 @@
#if SD_UIKIT
#import "UIView+WebCacheOperation.h"
#define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage"
#import "UIView+WebCache.h"
@implementation UIImageView (HighlightedWebCache)
@ -36,41 +35,16 @@
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_cancelCurrentHighlightedImageLoad];
if (url) {
__weak __typeof(self)wself = self;
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe (^{
if (!wself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
wself.highlightedImage = image;
[wself setNeedsLayout];
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey];
} else {
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
- (void)sd_cancelCurrentHighlightedImageLoad {
[self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey];
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:nil
options:options
operationKey:@"UIImageViewImageOperationHighlighted"
setImageBlock:^(UIImage *image, NSData *imageData) {
weakSelf.highlightedImage = image;
}
progress:progressBlock
completed:completedBlock];
}
@end

View File

@ -47,14 +47,6 @@
*/
@interface UIImageView (WebCache)
/**
* Get the current image URL.
*
* Note that because of the limitations of categories this property can get out of sync
* if you use setImage: directly.
*/
- (nullable NSURL *)sd_imageURL;
/**
* Set the imageView `image` with an `url`.
*
@ -183,37 +175,18 @@
completed:(nullable SDExternalCompletionBlock)completedBlock;
#if SD_UIKIT
#pragma mark - Animation of multiple images
/**
* Download an array of images and starts them in an animation loop
*
* @param arrayOfURLs An array of NSURL
*/
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs;
#endif
/**
* Cancel the current download
*/
- (void)sd_cancelCurrentImageLoad;
#if SD_UIKIT
- (void)sd_cancelCurrentAnimationImagesLoad;
/**
* Show activity UIActivityIndicatorView
*/
- (void)setShowActivityIndicatorView:(BOOL)show;
/**
* set desired UIActivityIndicatorViewStyle
*
* @param style The style of the UIActivityIndicatorView
*/
- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
- (BOOL)showActivityIndicatorView;
- (void)addActivityIndicator;
- (void)removeActivityIndicator;
#endif
@end

View File

@ -12,13 +12,7 @@
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"
static char imageURLKey;
#if SD_UIKIT
static char TAG_ACTIVITY_INDICATOR;
static char TAG_ACTIVITY_STYLE;
#endif
static char TAG_ACTIVITY_SHOW;
#import "UIView+WebCache.h"
@implementation UIImageView (WebCache)
@ -51,65 +45,13 @@ static char TAG_ACTIVITY_SHOW;
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
[self sd_cancelCurrentImageLoad];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
if (url) {
// check if activityView is enabled or not
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
[wself removeActivityIndicator];
if (!wself) return;
dispatch_main_sync_safe(^{
if (!wself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
wself.image = image;
#if SD_UIKIT
[wself setNeedsLayout];
#elif SD_MAC
[wself setNeedsLayout:YES];
#endif
} else {
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
#if SD_UIKIT
[wself setNeedsLayout];
#elif SD_MAC
[wself setNeedsLayout:YES];
#endif
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
} else {
dispatch_main_async_safe(^{
[self removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:nil
progress:progressBlock
completed:completedBlock];
}
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
@ -118,16 +60,15 @@ static char TAG_ACTIVITY_SHOW;
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key];
UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];
[self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];
}
- (nullable NSURL *)sd_imageURL {
return objc_getAssociatedObject(self, &imageURLKey);
}
#if SD_UIKIT
#pragma mark - Animation of multiple images
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs {
[self sd_cancelCurrentAnimationImagesLoad];
__weak __typeof(self)wself = self;
@ -158,88 +99,12 @@ static char TAG_ACTIVITY_SHOW;
[self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
}
#endif
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
}
#if SD_UIKIT
- (void)sd_cancelCurrentAnimationImagesLoad {
[self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
}
#endif
#pragma mark -
#if SD_UIKIT
- (UIActivityIndicatorView *)activityIndicator {
return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
}
- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
}
#endif
- (void)setShowActivityIndicatorView:(BOOL)show {
objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)showActivityIndicatorView {
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
}
#if SD_UIKIT
- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
}
- (int)getIndicatorStyle{
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
}
#endif
- (void)addActivityIndicator {
#if SD_UIKIT
if (!self.activityIndicator) {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]];
self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
dispatch_main_async_safe(^{
[self addSubview:self.activityIndicator];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
});
}
dispatch_main_async_safe(^{
[self.activityIndicator startAnimating];
});
#endif
}
- (void)removeActivityIndicator {
#if SD_UIKIT
if (self.activityIndicator) {
[self.activityIndicator removeFromSuperview];
self.activityIndicator = nil;
}
#endif
}
@end
#endif

View File

@ -0,0 +1,81 @@
/*
* 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 "SDWebImageCompat.h"
#if SD_UIKIT || SD_MAC
#import "SDWebImageManager.h"
typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
@interface UIView (WebCache)
/**
* Get the current image URL.
*
* Note that because of the limitations of categories this property can get out of sync
* if you use setImage: directly.
*/
- (nullable NSURL *)sd_imageURL;
/**
* Set the imageView `image` with an `url` and optionally a placeholder image.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
* @param operationKey A string to be used as the operation key. If nil, will use the class name
* @param setImageBlock Block used for custom set image code
* @param progressBlock A block called while image is downloading
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
/**
* Cancel the current download
*/
- (void)sd_cancelCurrentImageLoad;
#if SD_UIKIT
#pragma mark - Activity indicator
/**
* Show activity UIActivityIndicatorView
*/
- (void)setShowActivityIndicatorView:(BOOL)show;
/**
* set desired UIActivityIndicatorViewStyle
*
* @param style The style of the UIActivityIndicatorView
*/
- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
- (BOOL)showActivityIndicatorView;
- (void)addActivityIndicator;
- (void)removeActivityIndicator;
#endif
@end
#endif

View File

@ -0,0 +1,195 @@
/*
* 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 "UIView+WebCache.h"
#if SD_UIKIT || SD_MAC
#import "objc/runtime.h"
#import "UIView+WebCacheOperation.h"
static char imageURLKey;
#if SD_UIKIT
static char TAG_ACTIVITY_INDICATOR;
static char TAG_ACTIVITY_STYLE;
#endif
static char TAG_ACTIVITY_SHOW;
@implementation UIView (WebCache)
- (nullable NSURL *)sd_imageURL {
return objc_getAssociatedObject(self, &imageURLKey);
}
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
if (url) {
// check if activityView is enabled or not
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
__strong __typeof (wself) sself = wself;
[sself removeActivityIndicator];
if (!sself) {
return;
}
dispatch_main_sync_safe(^{
if (!sself) {
return;
}
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
} else {
if ((options & SDWebImageDelayPlaceholder)) {
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
dispatch_main_async_safe(^{
[self removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
}
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
if (setImageBlock) {
setImageBlock(image, imageData);
return;
}
#if SD_UIKIT
if ([self isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)self;
imageView.image = image;
} else if ([self isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)self;
[button setImage:image forState:UIControlStateNormal];
}
#endif
}
- (void)sd_setNeedsLayout {
#if SD_UIKIT
[self setNeedsLayout];
#elif SD_MAC
[self setNeedsLayout:YES];
#endif
}
#pragma mark - Activity indicator
#pragma mark -
#if SD_UIKIT
- (UIActivityIndicatorView *)activityIndicator {
return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
}
- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
}
#endif
- (void)setShowActivityIndicatorView:(BOOL)show {
objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)showActivityIndicatorView {
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
}
#if SD_UIKIT
- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
}
- (int)getIndicatorStyle{
return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
}
#endif
- (void)addActivityIndicator {
#if SD_UIKIT
if (!self.activityIndicator) {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]];
self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
dispatch_main_async_safe(^{
[self addSubview:self.activityIndicator];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
});
}
dispatch_main_async_safe(^{
[self.activityIndicator startAnimating];
});
#endif
}
- (void)removeActivityIndicator {
#if SD_UIKIT
if (self.activityIndicator) {
[self.activityIndicator removeFromSuperview];
self.activityIndicator = nil;
}
#endif
}
@end
#endif

View File

@ -1,11 +1,15 @@
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
xcodeproj 'SDWebImage Tests'
workspace '../SDWebImage'
target 'Tests' do
platform :ios, '7.0'
pod 'Expecta', '<=0.3.1'
pod 'SDWebImage', :path => '../'
platform :ios, '8.0'
pod 'Expecta'
pod 'SDWebImage/WebP', :path => '../'
pod 'SDWebImage/MapKit', :path => '../'
pod 'SDWebImage/GIF', :path => '../'
end

View File

@ -7,8 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
0D87E1F83BD319CEC7622E9F /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0462A7F023A057322E59B3C5 /* libPods-Tests.a */; };
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.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 */; };
433BBBBB1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 433BBBBA1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg */; };
4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C1D01D97F80F007E863A /* SDWebImagePrefetcherTests.m */; };
4369C2741D9804B1007E863A /* SDCategoriesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2731D9804B1007E863A /* SDCategoriesTests.m */; };
53F0240D24D359127872F512 /* Pods_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DAAA77E3CA7387F702040D9 /* Pods_Tests.framework */; };
5F7F38AD1AE2A77A00B0E330 /* TestImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */; };
DA248D57195472AA00390AB0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D56195472AA00390AB0 /* XCTest.framework */; };
DA248D59195472AA00390AB0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D58195472AA00390AB0 /* Foundation.framework */; };
@ -20,8 +26,14 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0462A7F023A057322E59B3C5 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
1DAAA77E3CA7387F702040D9 /* Pods_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderTests.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>"; };
433BBBBA1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = MonochromeTestImage.jpg; sourceTree = "<group>"; };
4369C1D01D97F80F007E863A /* SDWebImagePrefetcherTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImagePrefetcherTests.m; sourceTree = "<group>"; };
4369C2731D9804B1007E863A /* SDCategoriesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDCategoriesTests.m; sourceTree = "<group>"; };
5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TestImage.jpg; sourceTree = "<group>"; };
700B00151041D7EE118B1ABD /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = "<group>"; };
A0085854E7D88C98F2F6C9FC /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = "<group>"; };
@ -45,7 +57,7 @@
DA248D57195472AA00390AB0 /* XCTest.framework in Frameworks */,
DA248D5B195472AA00390AB0 /* UIKit.framework in Frameworks */,
DA248D59195472AA00390AB0 /* Foundation.framework in Frameworks */,
0D87E1F83BD319CEC7622E9F /* libPods-Tests.a in Frameworks */,
53F0240D24D359127872F512 /* Pods_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -85,7 +97,7 @@
DA248D56195472AA00390AB0 /* XCTest.framework */,
DA248D58195472AA00390AB0 /* Foundation.framework */,
DA248D5A195472AA00390AB0 /* UIKit.framework */,
0462A7F023A057322E59B3C5 /* libPods-Tests.a */,
1DAAA77E3CA7387F702040D9 /* Pods_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -93,12 +105,18 @@
DA248D5C195472AA00390AB0 /* Tests */ = {
isa = PBXGroup;
children = (
433BBBBA1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg */,
433BBBB61D7EF8200086B6E9 /* TestImage.gif */,
5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */,
433BBBB81D7EF8260086B6E9 /* TestImage.png */,
DA248D5D195472AA00390AB0 /* Supporting Files */,
DA248D68195475D800390AB0 /* SDImageCacheTests.m */,
DA248D6A195476AC00390AB0 /* SDWebImageManagerTests.m */,
DA91BEBB19795BC9006F2536 /* UIImageMultiFormatTests.m */,
1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */,
433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */,
4369C1D01D97F80F007E863A /* SDWebImagePrefetcherTests.m */,
4369C2731D9804B1007E863A /* SDCategoriesTests.m */,
);
path = Tests;
sourceTree = "<group>";
@ -120,12 +138,12 @@
isa = PBXNativeTarget;
buildConfigurationList = DA248D67195472AA00390AB0 /* Build configuration list for PBXNativeTarget "Tests" */;
buildPhases = (
09522B7196293172D6408744 /* 📦 Check Pods Manifest.lock */,
09522B7196293172D6408744 /* [CP] Check Pods Manifest.lock */,
DA248D4F195472AA00390AB0 /* Sources */,
DA248D50195472AA00390AB0 /* Frameworks */,
DA248D51195472AA00390AB0 /* Resources */,
C86216497B5A0BA9501E2C07 /* 📦 Embed Pods Frameworks */,
85E5D3885A03BFC23B050908 /* 📦 Copy Pods Resources */,
C86216497B5A0BA9501E2C07 /* [CP] Embed Pods Frameworks */,
85E5D3885A03BFC23B050908 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -167,36 +185,39 @@
buildActionMask = 2147483647;
files = (
5F7F38AD1AE2A77A00B0E330 /* TestImage.jpg in Resources */,
433BBBB71D7EF8200086B6E9 /* TestImage.gif in Resources */,
DA248D61195472AA00390AB0 /* InfoPlist.strings in Resources */,
433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */,
433BBBBB1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
09522B7196293172D6408744 /* 📦 Check Pods Manifest.lock */ = {
09522B7196293172D6408744 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\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 /* 📦 Copy Pods Resources */ = {
85E5D3885A03BFC23B050908 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -204,14 +225,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests/Pods-Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C86216497B5A0BA9501E2C07 /* 📦 Embed Pods Frameworks */ = {
C86216497B5A0BA9501E2C07 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -227,8 +248,11 @@
buildActionMask = 2147483647;
files = (
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */,
4369C2741D9804B1007E863A /* SDCategoriesTests.m in Sources */,
4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */,
DA248D69195475D800390AB0 /* SDImageCacheTests.m in Sources */,
DA248D6B195476AC00390AB0 /* SDWebImageManagerTests.m in Sources */,
433BBBB51D7EF5C00086B6E9 /* SDWebImageDecoderTests.m in Sources */,
DA91BEBC19795BC9006F2536 /* UIImageMultiFormatTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -251,7 +275,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ENABLE_TESTABILITY = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
ONLY_ACTIVE_ARCH = YES;
};
name = Debug;
@ -259,7 +283,7 @@
DA248D4B1954721A00390AB0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
};
name = Release;
};

View File

@ -10,7 +10,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,146 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#define EXP_SHORTHAND // required by Expecta
#import <XCTest/XCTest.h>
#import <Expecta/Expecta.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import <SDWebImage/UIImageView+HighlightedWebCache.h>
#import <SDWebImage/MKAnnotationView+WebCache.h>
#import <SDWebImage/UIButton+WebCache.h>
#import <SDWebImage/FLAnimatedImageView+WebCache.h>
@import FLAnimatedImage;
@interface SDCategoriesTests : XCTestCase
@end
@implementation SDCategoriesTests
- (void)testUIImageViewSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIImageView setImageWithURL"];
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage050.jpg"];
[imageView sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.image).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testUIImageViewSetHighlightedImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIImageView setHighlightedImageWithURL"];
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage051.jpg"];
[imageView sd_setHighlightedImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.highlightedImage).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testMKAnnotationViewSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"MKAnnotationView setImageWithURL"];
MKAnnotationView *annotationView = [[MKAnnotationView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage052.jpg"];
[annotationView sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(annotationView.image).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testUIButtonSetImageWithURLNormalState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setImageWithURL normalState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage053.jpg"];
[button sd_setImageWithURL:originalImageURL
forState:UIControlStateNormal
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button imageForState:UIControlStateNormal]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testUIButtonSetImageWithURLHighlightedState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setImageWithURL highlightedState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage054.jpg"];
[button sd_setImageWithURL:originalImageURL
forState:UIControlStateHighlighted
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button imageForState:UIControlStateHighlighted]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testUIButtonSetBackgroundImageWithURLNormalState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setBackgroundImageWithURL normalState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage055.jpg"];
[button sd_setBackgroundImageWithURL:originalImageURL
forState:UIControlStateNormal
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button backgroundImageForState:UIControlStateNormal]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testFLAnimatedImageViewSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"FLAnimatedImageView setImageWithURL"];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"];
[imageView sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.image).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
@end

View File

@ -10,9 +10,9 @@
#import <XCTest/XCTest.h>
#import <Expecta.h>
#import <Expecta/Expecta.h>
#import "SDImageCache.h"
#import <SDWebImage/SDImageCache.h>
NSString *kImageTestKey = @"TestImageKey.jpg";
@ -22,172 +22,179 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
@implementation SDImageCacheTests
- (void)setUp
{
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
self.sharedImageCache = [SDImageCache sharedImageCache];
[self clearAllCaches];
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testSharedImageCache {
- (void)test01SharedImageCache {
expect(self.sharedImageCache).toNot.beNil();
}
- (void)testSingleton{
- (void)test02Singleton{
expect(self.sharedImageCache).to.equal([SDImageCache sharedImageCache]);
}
- (void)testClearDiskCache{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
[self.sharedImageCache clearDiskOnCompletion:^{
expect([self.sharedImageCache diskImageExistsWithKey:kImageTestKey]).to.equal(NO);
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]);
}];
- (void)test03ImageCacheCanBeInstantiated {
SDImageCache *imageCache = [[SDImageCache alloc] init];
expect(imageCache).toNot.equal([SDImageCache sharedImageCache]);
}
- (void)testClearMemoryCache{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test04ClearDiskCache{
XCTestExpectation *expectation = [self expectationWithDescription:@"Clear disk cache"];
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
[self.sharedImageCache clearDiskOnCompletion:^{
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
if (!isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should not be in cache");
}
}];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]);
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test05ClearMemoryCache{
XCTestExpectation *expectation = [self expectationWithDescription:@"Clear memory cache"];
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
[self.sharedImageCache clearMemory];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
// Seems not able to access the files correctly (maybe only from test?)
//expect([self.sharedImageCache diskImageExistsWithKey:kImageTestKey]).to.equal(YES);
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
expect(isInCache).to.equal(YES);
if (isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should be in cache");
}
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
// Testing storeImage:forKey:
- (void)testInsertionOfImage {
- (void)test06InsertionOfImage {
XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey"];
UIImage *image = [self imageForTesting];
[self.sharedImageCache storeImage:image forKey:kImageTestKey];
[self.sharedImageCache storeImage:image forKey:kImageTestKey completion:nil];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal(image);
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).to.equal(image);
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
if (isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should be in cache");
}
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
// Testing storeImage:forKey:toDisk:YES
- (void)testInsertionOfImageForcingDiskStorage{
- (void)test07InsertionOfImageForcingDiskStorage{
XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey toDisk=YES"];
UIImage *image = [self imageForTesting];
[self.sharedImageCache storeImage:image forKey:kImageTestKey toDisk:YES];
[self.sharedImageCache storeImage:image forKey:kImageTestKey toDisk:YES completion:nil];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal(image);
// Seems not able to access the files correctly (maybe only from test?)
//expect([self.sharedImageCache diskImageExistsWithKey:kImageTestKey]).to.equal(YES);
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
expect(isInCache).to.equal(YES);
if (isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should be in cache");
}
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
// Testing storeImage:forKey:toDisk:NO
- (void)testInsertionOfImageOnlyInMemory {
- (void)test08InsertionOfImageOnlyInMemory {
XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey toDisk=NO"];
UIImage *image = [self imageForTesting];
[self.sharedImageCache storeImage:image forKey:@"TestImage" toDisk:NO];
[self.sharedImageCache diskImageExistsWithKey:@"TestImage" completion:^(BOOL isInCache) {
expect(isInCache).to.equal(YES);
[self.sharedImageCache storeImage:image forKey:kImageTestKey toDisk:NO completion:nil];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]);
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
if (!isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should not be in cache");
}
}];
[self.sharedImageCache clearMemory];
[self.sharedImageCache diskImageExistsWithKey:@"TestImage" completion:^(BOOL isInCache) {
expect(isInCache).to.equal(NO);
}];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil();
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testRetrievalImageThroughNSOperation{
//- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
- (void)test09RetrieveImageThroughNSOperation{
//- (NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
UIImage *imageForTesting = [self imageForTesting];
[self.sharedImageCache storeImage:imageForTesting forKey:kImageTestKey];
NSOperation *operation = [self.sharedImageCache queryDiskCacheForKey:kImageTestKey done:^(UIImage *image, NSData *data, SDImageCacheType cacheType) {
[self.sharedImageCache storeImage:imageForTesting forKey:kImageTestKey completion:nil];
NSOperation *operation = [self.sharedImageCache queryCacheOperationForKey:kImageTestKey done:^(UIImage *image, NSData *data, SDImageCacheType cacheType) {
expect(image).to.equal(imageForTesting);
}];
expect(operation).toNot.beNil;
}
- (void)testRemoveImageForKey{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
[self.sharedImageCache removeImageForKey:kImageTestKey];
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).to.beNil;
}
- (void)testRemoveImageForKeyWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test10RemoveImageForKeyWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
[self.sharedImageCache removeImageForKey:kImageTestKey withCompletion:^{
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).to.beNil;
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
}];
}
- (void)testRemoveImageForKeyNotFromDisk{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
[self.sharedImageCache removeImageForKey:kImageTestKey fromDisk:NO];
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).toNot.beNil;
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
}
- (void)testRemoveImageForKeyFromDisk{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
[self.sharedImageCache removeImageForKey:kImageTestKey fromDisk:NO];
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).to.beNil;
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
}
- (void)testRemoveImageforKeyNotFromDiskWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test11RemoveImageforKeyNotFromDiskWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
[self.sharedImageCache removeImageForKey:kImageTestKey fromDisk:NO withCompletion:^{
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).toNot.beNil;
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
}];
}
- (void)testRemoveImageforKeyFromDiskWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test12RemoveImageforKeyFromDiskWithCompletion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
[self.sharedImageCache removeImageForKey:kImageTestKey fromDisk:YES withCompletion:^{
expect([self.sharedImageCache imageFromDiskCacheForKey:kImageTestKey]).to.beNil;
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
}];
}
// TODO -- Testing insertion with recalculate
- (void)testInsertionOfImageOnlyInDisk {
}
- (void)testInitialCacheSize{
- (void)test20InitialCacheSize{
expect([self.sharedImageCache getSize]).to.equal(0);
}
- (void)testInitialDiskCount{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test21InitialDiskCount{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
expect([self.sharedImageCache getDiskCount]).to.equal(1);
}
- (void)testDiskCountAfterInsertion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test22DiskCountAfterInsertion{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
expect([self.sharedImageCache getDiskCount]).to.equal(1);
}
- (void)testDefaultCachePathForAnyKey{
- (void)test31DefaultCachePathForAnyKey{
NSString *path = [self.sharedImageCache defaultCachePathForKey:kImageTestKey];
expect(path).toNot.beNil;
}
- (void)testCachePathForNonExistingKey{
- (void)test32CachePathForNonExistingKey{
NSString *path = [self.sharedImageCache cachePathForKey:kImageTestKey inPath:[self.sharedImageCache defaultCachePathForKey:kImageTestKey]];
expect(path).to.beNil;
}
- (void)testCachePathForExistingKey{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey];
- (void)test33CachePathForExistingKey{
[self.sharedImageCache storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil];
NSString *path = [self.sharedImageCache cachePathForKey:kImageTestKey inPath:[self.sharedImageCache defaultCachePathForKey:kImageTestKey]];
expect(path).notTo.beNil;
}
// TODO -- Testing image data insertion
- (void)testInsertionOfImageData {
- (void)test40InsertionOfImageData {
NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]];
[self.sharedImageCache storeImageDataToDisk:imageData forKey:kImageTestKey];
@ -202,18 +209,28 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
[self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
expect(isInCache).to.equal(YES);
}];
[self.sharedImageCache calculateSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize) {
expect(fileCount).to.beLessThan(100);
}];
}
#pragma mark Helper methods
- (void)clearAllCaches{
[self.sharedImageCache clearDisk];
[self.sharedImageCache deleteOldFilesWithCompletionBlock:nil];
// TODO: this is not ok, clearDiskOnCompletion will clear async, this means that when we execute the tests, the cache might not be cleared
[self.sharedImageCache clearDiskOnCompletion:nil];
[self.sharedImageCache clearMemory];
}
- (UIImage *)imageForTesting{
return [UIImage imageWithContentsOfFile:[self testImagePath]];
static UIImage *reusableImage = nil;
if (!reusableImage) {
reusableImage = [UIImage imageWithContentsOfFile:[self testImagePath]];
}
return reusableImage;
}
- (NSString *)testImagePath {

View File

@ -0,0 +1,60 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#define EXP_SHORTHAND // required by Expecta
#import <XCTest/XCTest.h>
#import <Expecta/Expecta.h>
#import <SDWebImage/SDWebImageDecoder.h>
@interface SDWebImageDecoderTests : XCTestCase
@end
@implementation SDWebImageDecoderTests
- (void)test01ThatDecodedImageWithNilImageReturnsNil {
expect([UIImage decodedImageWithImage:nil]).to.beNil();
}
- (void)test02ThatDecodedImageWithImageWorksWithARegularJPGImage {
NSString * testImagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestImage" ofType:@"jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:testImagePath];
UIImage *decodedImage = [UIImage decodedImageWithImage:image];
expect(decodedImage).toNot.beNil();
expect(decodedImage).toNot.equal(image);
}
- (void)test03ThatDecodedImageWithImageDoesNotDecodeAnimatedImages {
NSString * testImagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestImage" ofType:@"gif"];
UIImage *image = [UIImage imageWithContentsOfFile:testImagePath];
UIImage *animatedImage = [UIImage animatedImageWithImages:@[image] duration:0];
UIImage *decodedImage = [UIImage decodedImageWithImage:animatedImage];
expect(decodedImage).toNot.beNil();
expect(decodedImage).to.equal(animatedImage);
}
- (void)test04ThatDecodedImageWithImageDoesNotDecodeImagesWithAlpha {
NSString * testImagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestImage" ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:testImagePath];
UIImage *decodedImage = [UIImage decodedImageWithImage:image];
expect(decodedImage).toNot.beNil();
expect(decodedImage).to.equal(image);
}
- (void)test05ThatDecodedImageWithImageWorksEvenWithMonochromeImage {
NSString * testImagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"MonochromeTestImage" ofType:@"jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:testImagePath];
UIImage *decodedImage = [UIImage decodedImageWithImage:image];
expect(decodedImage).toNot.beNil();
expect(decodedImage).toNot.equal(image);
}
@end

View File

@ -11,9 +11,49 @@
#import <XCTest/XCTest.h>
#import <Expecta.h>
#import <Expecta/Expecta.h>
#import <SDWebImage/SDWebImageDownloader.h>
#import <SDWebImage/SDWebImageDownloaderOperation.h>
/**
* Category for SDWebImageDownloader so we can access the operationClass
*/
@interface SDWebImageDownloader ()
@property (assign, nonatomic, nullable) Class operationClass;
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback;
@end
/**
* A class that fits the NSOperation+SDWebImageDownloaderOperationInterface requirement so we can test
*/
@interface CustomDownloaderOperation : NSOperation<SDWebImageDownloaderOperationInterface>
@property (nonatomic, assign) BOOL shouldDecompressImages;
@property (nonatomic, strong, nullable) NSURLCredential *credential;
@end
@implementation CustomDownloaderOperation
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)req inSession:(nullable NSURLSession *)ses options:(SDWebImageDownloaderOptions)opt {
if ((self = [super init])) { }
return self;
}
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
return nil;
}
@end
#import "SDWebImageDownloader.h"
@interface SDWebImageDownloaderTests : XCTestCase
@ -21,68 +61,288 @@
@implementation SDWebImageDownloaderTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
- (void)test01ThatSharedDownloaderIsNotEqualToInitDownloader {
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
expect(downloader).toNot.equal([SDWebImageDownloader sharedDownloader]);
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
- (void)test02ThatByDefaultDownloaderSetsTheAcceptHTTPHeader {
expect([[SDWebImageDownloader sharedDownloader] valueForHTTPHeaderField:@"Accept"]).to.match(@"image/\\*");
}
- (void)testThatDownloadingSameURLTwiceAndCancellingFirstWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Correct image downloads"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage000.jpg"];
id token1 = [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
XCTFail(@"Shouldn't have completed here.");
}];
expect(token1).toNot.beNil();
id token2 = [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
[expectation fulfill];
}];
expect(token2).toNot.beNil();
[[SDWebImageDownloader sharedDownloader] cancel:token1];
[self waitForExpectationsWithTimeout:5. handler:nil];
- (void)test03ThatSetAndGetValueForHTTPHeaderFieldWork {
NSString *headerValue = @"Tests";
NSString *headerName = @"AppName";
// set it
[[SDWebImageDownloader sharedDownloader] setValue:headerValue forHTTPHeaderField:headerName];
expect([[SDWebImageDownloader sharedDownloader] valueForHTTPHeaderField:headerName]).to.equal(headerValue);
// clear it
[[SDWebImageDownloader sharedDownloader] setValue:nil forHTTPHeaderField:headerName];
expect([[SDWebImageDownloader sharedDownloader] valueForHTTPHeaderField:headerName]).to.beNil();
}
- (void)testThatCancelingDownloadThenRequestingAgainWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Correct image downloads"];
- (void)test04ThatASimpleDownloadWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Simple download"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage004.jpg"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test05ThatSetAndGetMaxConcurrentDownloadsWorks {
NSInteger initialValue = [SDWebImageDownloader sharedDownloader].maxConcurrentDownloads;
[[SDWebImageDownloader sharedDownloader] setMaxConcurrentDownloads:3];
expect([SDWebImageDownloader sharedDownloader].maxConcurrentDownloads).to.equal(3);
[[SDWebImageDownloader sharedDownloader] setMaxConcurrentDownloads:initialValue];
}
- (void)test06ThatUsingACustomDownloaderOperationWorks {
// we try to set a usual NSOperation as operation class. Should not work
[[SDWebImageDownloader sharedDownloader] setOperationClass:[NSOperation class]];
expect([SDWebImageDownloader sharedDownloader].operationClass).to.equal([SDWebImageDownloaderOperation class]);
// setting an NSOperation subclass that conforms to SDWebImageDownloaderOperationInterface - should work
[[SDWebImageDownloader sharedDownloader] setOperationClass:[CustomDownloaderOperation class]];
expect([SDWebImageDownloader sharedDownloader].operationClass).to.equal([CustomDownloaderOperation class]);
// back to the original value
[[SDWebImageDownloader sharedDownloader] setOperationClass:nil];
expect([SDWebImageDownloader sharedDownloader].operationClass).to.equal([SDWebImageDownloaderOperation class]);
}
- (void)test07ThatAddProgressCallbackCompletedBlockWithNilURLCallsTheCompletionBlockWithNils {
XCTestExpectation *expectation = [self expectationWithDescription:@"Completion is called with nils"];
[[SDWebImageDownloader sharedDownloader] addProgressCallback:nil completedBlock:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (!image && !data && !error) {
[expectation fulfill];
} else {
XCTFail(@"All params should be nil");
}
} forURL:nil createCallback:nil];
[self waitForExpectationsWithTimeout:0.5 handler:nil];
}
- (void)test08ThatAHTTPAuthDownloadWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP Auth download"];
[SDWebImageDownloader sharedDownloader].username = @"httpwatch";
[SDWebImageDownloader sharedDownloader].password = @"httpwatch01";
NSURL *imageURL = [NSURL URLWithString:@"http://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.35786508303135633"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
[SDWebImageDownloader sharedDownloader].username = nil;
[SDWebImageDownloader sharedDownloader].password = nil;
}
- (void)test09ThatProgressiveJPEGWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Progressive JPEG download"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage009.jpg"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:SDWebImageDownloaderProgressiveDownload progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else if (finished) {
XCTFail(@"Something went wrong");
} else {
// progressive updates
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test10That404CaseCallsCompletionWithError {
NSURL *imageURL = [NSURL URLWithString:@"http://static2.dmcdn.net/static/video/656/177/44771656:jpeg_preview_small.jpg?20120509154705"];
XCTestExpectation *expectation = [self expectationWithDescription:@"404"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (!image && !data && error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
id token1 = [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
XCTFail(@"Shouldn't have completed here.");
}];
- (void)test11ThatCancelWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Cancel"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage011.jpg"];
SDWebImageDownloadToken *token = [[SDWebImageDownloader sharedDownloader]
downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
XCTFail(@"Should not get here");
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[[SDWebImageDownloader sharedDownloader] cancel:token];
// doesn't cancel immediately - since it uses dispatch async
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(0);
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test12ThatWeCanUseAnotherSessionForEachDownloadOperation {
XCTestExpectation *expectation = [self expectationWithDescription:@"Owned session"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage012.jpg"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:imageURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
request.HTTPShouldUsePipelining = YES;
request.allHTTPHeaderFields = @{@"Accept": @"image/*;q=0.8"};
SDWebImageDownloaderOperation *operation = [[SDWebImageDownloaderOperation alloc] initWithRequest:request
inSession:nil
options:0];
[operation addHandlersForProgress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
[operation start];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test13ThatDownloadCanContinueWhenTheAppEntersBackground {
XCTestExpectation *expectation = [self expectationWithDescription:@"Simple download"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage013.jpg"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:SDWebImageDownloaderContinueInBackground progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test14ThatPNGWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"WEBP"];
NSURL *imageURL = [NSURL URLWithString:@"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test15ThatWEBPWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"WEBP"];
NSURL *imageURL = [NSURL URLWithString:@"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp"];
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
/**
* Per #883 - Fix multiple requests for same image and then canceling one
* Old SDWebImage (3.x) could not handle correctly multiple requests for the same image + cancel
* In 4.0, via #883 added `SDWebImageDownloadToken` so we can cancel exactly the request we want
* This test validates the scenario of making 2 requests for the same image and cancelling the 1st one
*/
- (void)test20ThatDownloadingSameURLTwiceAndCancellingFirstWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Correct image downloads"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage020.jpg"];
SDWebImageDownloadToken *token1 = [[SDWebImageDownloader sharedDownloader]
downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
XCTFail(@"Shouldn't have completed here.");
}];
expect(token1).toNot.beNil();
SDWebImageDownloadToken *token2 = [[SDWebImageDownloader sharedDownloader]
downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
XCTFail(@"Something went wrong");
}
}];
expect(token2).toNot.beNil();
[[SDWebImageDownloader sharedDownloader] cancel:token1];
id token2 = [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
[expectation fulfill];
}];
expect(token2).toNot.beNil();
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
[self waitForExpectationsWithTimeout:5. handler:nil];
/**
* Per #883 - Fix multiple requests for same image and then canceling one
* Old SDWebImage (3.x) could not handle correctly multiple requests for the same image + cancel
* In 4.0, via #883 added `SDWebImageDownloadToken` so we can cancel exactly the request we want
* This test validates the scenario of requesting an image, cancel and then requesting it again
*/
- (void)test21ThatCancelingDownloadThenRequestingAgainWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Correct image downloads"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage021.jpg"];
SDWebImageDownloadToken *token1 = [[SDWebImageDownloader sharedDownloader]
downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
XCTFail(@"Shouldn't have completed here.");
}];
expect(token1).toNot.beNil();
[[SDWebImageDownloader sharedDownloader] cancel:token1];
SDWebImageDownloadToken *token2 = [[SDWebImageDownloader sharedDownloader]
downloadImageWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (image && data && !error && finished) {
[expectation fulfill];
} else {
NSLog(@"image = %@, data = %@, error = %@", image, data, error);
XCTFail(@"Something went wrong");
}
}];
expect(token2).toNot.beNil();
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
@end

View File

@ -10,12 +10,11 @@
#import <XCTest/XCTest.h>
#import <Expecta.h>
#import <Expecta/Expecta.h>
#import "SDWebImageManager.h"
static int64_t kAsyncTestTimeout = 5;
#import <SDWebImage/SDWebImageManager.h>
NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage001.jpg";
@interface SDWebImageManagerTests : XCTestCase
@ -23,22 +22,15 @@ static int64_t kAsyncTestTimeout = 5;
@implementation SDWebImageManagerTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
- (void)test01ThatSharedManagerIsNotEqualToInitManager {
SDWebImageManager *manager = [[SDWebImageManager alloc] init];
expect(manager).toNot.equal([SDWebImageManager sharedManager]);
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testThatDownloadInvokesCompletionBlockWithCorrectParamsAsync {
- (void)test02ThatDownloadInvokesCompletionBlockWithCorrectParamsAsync {
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image download completes"];
NSURL *originalImageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage001.jpg"];
NSURL *originalImageURL = [NSURL URLWithString:workingImageURL];
[[SDWebImageManager sharedManager] loadImageWithURL:originalImageURL
options:SDWebImageRefreshCached
@ -51,11 +43,12 @@ static int64_t kAsyncTestTimeout = 5;
[expectation fulfill];
expectation = nil;
}];
expect([[SDWebImageManager sharedManager] isRunning]).to.equal(YES);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testThatDownloadWithIncorrectURLInvokesCompletionBlockWithAnErrorAsync {
- (void)test03ThatDownloadWithIncorrectURLInvokesCompletionBlockWithAnErrorAsync {
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image download completes"];
NSURL *originalImageURL = [NSURL URLWithString:@"http://static2.dmcdn.net/static/video/656/177/44771656:jpeg_preview_small.png"];
@ -75,4 +68,49 @@ static int64_t kAsyncTestTimeout = 5;
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test04CachedImageExistsForURL {
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image exists in cache"];
NSURL *imageURL = [NSURL URLWithString:workingImageURL];
[[SDWebImageManager sharedManager] cachedImageExistsForURL:imageURL completion:^(BOOL isInCache) {
if (isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should be in cache");
}
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test05DiskImageExistsForURL {
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image exists in disk cache"];
NSURL *imageURL = [NSURL URLWithString:workingImageURL];
[[SDWebImageManager sharedManager] diskImageExistsForURL:imageURL completion:^(BOOL isInCache) {
if (isInCache) {
[expectation fulfill];
} else {
XCTFail(@"Image should be in cache");
}
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test06CancellAll {
XCTestExpectation *expectation = [self expectationWithDescription:@"Cancel"];
NSURL *imageURL = [NSURL URLWithString:@"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage006.jpg"];
[[SDWebImageManager sharedManager] loadImageWithURL:imageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
XCTFail(@"Should not get here");
}];
[[SDWebImageManager sharedManager] cancelAll];
// doesn't cancel immediately - since it uses dispatch async
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
expect([[SDWebImageManager sharedManager] isRunning]).to.equal(NO);
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
@end

View File

@ -0,0 +1,71 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#define EXP_SHORTHAND // required by Expecta
#import <XCTest/XCTest.h>
#import <Expecta/Expecta.h>
#import <SDWebImage/SDWebImagePrefetcher.h>
@interface SDWebImagePrefetcherTests : XCTestCase
@end
@implementation SDWebImagePrefetcherTests
- (void)test01ThatSharedPrefetcherIsNotEqualToInitPrefetcher {
SDWebImagePrefetcher *prefetcher = [[SDWebImagePrefetcher alloc] init];
expect(prefetcher).toNot.equal([SDWebImagePrefetcher sharedImagePrefetcher]);
}
- (void)test02PrefetchMultipleImages {
XCTestExpectation *expectation = [self expectationWithDescription:@"Correct prefetch of multiple images"];
NSMutableArray *imageURLs = [NSMutableArray array];
for (int i=40; i<43; i++) {
NSString *imageURLString = [NSString stringWithFormat:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage%03d.jpg", i];
NSURL *imageURL = [NSURL URLWithString:imageURLString];
[imageURLs addObject:imageURL];
}
__block int numberOfPrefetched = 0;
[[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:imageURLs progress:^(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls) {
numberOfPrefetched += 1;
expect(numberOfPrefetched).to.equal(noOfFinishedUrls);
expect(noOfFinishedUrls).to.beLessThanOrEqualTo(noOfTotalUrls);
expect(noOfTotalUrls).to.equal(imageURLs.count);
} completed:^(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls) {
expect(numberOfPrefetched).to.equal(noOfFinishedUrls);
expect(noOfFinishedUrls).to.equal(imageURLs.count);
expect(noOfSkippedUrls).to.equal(0);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)test03PrefetchWithEmptyArrayWillCallTheCompletionWithAllZeros {
XCTestExpectation *expectation = [self expectationWithDescription:@"Prefetch with empty array"];
[[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:@[] progress:nil completed:^(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls) {
expect(noOfFinishedUrls).to.equal(0);
expect(noOfSkippedUrls).to.equal(0);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
// TODO: test the prefetcher delegate works
@end

BIN
Tests/Tests/TestImage.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
Tests/Tests/TestImage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -10,9 +10,9 @@
#import <XCTest/XCTest.h>
#import <Expecta.h>
#import <Expecta/Expecta.h>
#import "UIImage+MultiFormat.h"
#import <SDWebImage/UIImage+MultiFormat.h>
@interface UIImageMultiFormatTests : XCTestCase
@ -22,18 +22,6 @@
@implementation UIImageMultiFormatTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testImageOrientationFromImageDataWithInvalidData {
// sync download image
SEL selector = @selector(sd_imageOrientationFromImageData:);