mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-31 10:20:15 +00:00
Add default PINRemoteImageDownloader
This commit is contained in:
@@ -37,6 +37,9 @@ Pod::Spec.new do |spec|
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.h',
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.m',
|
||||
]
|
||||
|
||||
#Subspecs
|
||||
|
||||
spec.subspec 'ASDealloc2MainObject' do |mrr|
|
||||
mrr.requires_arc = false
|
||||
mrr.source_files = [
|
||||
@@ -45,6 +48,15 @@ Pod::Spec.new do |spec|
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.m',
|
||||
]
|
||||
end
|
||||
|
||||
spec.subspec 'PINRemoteImage' do |pin|
|
||||
pin.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) PIN_REMOTE_IMAGE=1' }
|
||||
pin.dependency 'PINRemoteImage'
|
||||
pin.dependency 'AsyncDisplayKit/ASDealloc2MainObject'
|
||||
end
|
||||
|
||||
# Include optional FLAnimatedImage module
|
||||
spec.default_subspec = 'PINRemoteImage'
|
||||
|
||||
spec.social_media_url = 'https://twitter.com/fbOpenSource'
|
||||
spec.library = 'c++'
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
#error ASMultiplexImageNode can be used on iOS 7, but must be linked against the iOS 8 SDK.
|
||||
#endif
|
||||
|
||||
#if PIN_REMOTE_IMAGE
|
||||
#import "ASPINRemoteImageDownloader.h"
|
||||
#else
|
||||
#import "ASBasicImageDownloader.h"
|
||||
#endif
|
||||
|
||||
NSString *const ASMultiplexImageNodeErrorDomain = @"ASMultiplexImageNodeErrorDomain";
|
||||
|
||||
static NSString *const kAssetsLibraryURLScheme = @"assets-library";
|
||||
@@ -72,7 +78,14 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
__weak NSOperation *_phImageRequestOperation;
|
||||
|
||||
// Networking.
|
||||
ASDN::Mutex _downloadIdentifierLock;
|
||||
id _downloadIdentifier;
|
||||
|
||||
//set on init only
|
||||
BOOL _downloaderSupportsNewProtocol;
|
||||
BOOL _downloaderImplementsSetProgress;
|
||||
BOOL _downloaderImplementsSetPriority;
|
||||
BOOL _cacherSupportsNewProtocol;
|
||||
}
|
||||
|
||||
//! @abstract Read-write redeclaration of property declared in ASMultiplexImageNode.h.
|
||||
@@ -162,6 +175,18 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
|
||||
_cache = cache;
|
||||
_downloader = downloader;
|
||||
|
||||
NSAssert([downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgress:completion:)] || [downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:)], @"downloader must respond to either downloadImageWithURL:callbackQueue:downloadProgress:completion: or downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:.");
|
||||
|
||||
_downloaderSupportsNewProtocol = [downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgress:completion:)] ? YES : NO;
|
||||
|
||||
NSAssert(cache == nil || [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)] || [cache respondsToSelector:@selector(fetchCachedImageWithURL:callbackQueue:completion:)], @"cacher must respond to either cachedImageWithURL:callbackQueue:completion: or fetchCachedImageWithURL:callbackQueue:completion:");
|
||||
|
||||
_downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)] ? YES : NO;
|
||||
_downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)] ? YES : NO;
|
||||
|
||||
_cacherSupportsNewProtocol = [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)] ? YES : NO;
|
||||
|
||||
self.shouldBypassEnsureDisplay = YES;
|
||||
|
||||
return self;
|
||||
@@ -169,8 +194,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
|
||||
return [self initWithCache:nil downloader:nil]; // satisfy compiler
|
||||
#if PIN_REMOTE_IMAGE
|
||||
return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]];
|
||||
#else
|
||||
return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
@@ -238,6 +266,48 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
}
|
||||
}
|
||||
|
||||
- (void)displayWillStart
|
||||
{
|
||||
[super displayWillStart];
|
||||
|
||||
[self fetchData];
|
||||
|
||||
if (_downloaderImplementsSetPriority) {
|
||||
{
|
||||
ASDN::MutexLocker l(_downloadIdentifierLock);
|
||||
if (_downloadIdentifier != nil) {
|
||||
[_downloader setPriority:ASImageDownloaderPriorityDisplay withDownloadIdentifier:_downloadIdentifier];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.image == nil) {
|
||||
if (_downloaderImplementsSetProgress) {
|
||||
{
|
||||
ASDN::MutexLocker l(_downloadIdentifierLock);
|
||||
|
||||
if (_downloadIdentifier != nil) {
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
[_downloader setProgressImageBlock:^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock);
|
||||
//Getting a result back for a different download identifier, download must not have been successfully canceled
|
||||
if (![strongSelf->_downloadIdentifier isEqual:downloadIdentifier] && downloadIdentifier != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
strongSelf.image = progressImage;
|
||||
} callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Core
|
||||
|
||||
- (void)setDelegate:(id <ASMultiplexImageNodeDelegate>)delegate
|
||||
@@ -331,10 +401,13 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
|
||||
- (void)_setDownloadIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
ASDN::MutexLocker l(_downloadIdentifierLock);
|
||||
if (ASObjectIsEqual(downloadIdentifier, _downloadIdentifier))
|
||||
return;
|
||||
|
||||
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
|
||||
if (_downloadIdentifier) {
|
||||
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
|
||||
}
|
||||
_downloadIdentifier = downloadIdentifier;
|
||||
}
|
||||
|
||||
@@ -622,10 +695,16 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required");
|
||||
|
||||
if (_cache) {
|
||||
[_cache fetchCachedImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() completion:^(CGImageRef coreGraphicsImageFromCache) {
|
||||
UIImage *imageFromCache = (coreGraphicsImageFromCache ? [UIImage imageWithCGImage:coreGraphicsImageFromCache] : nil);
|
||||
completionBlock(imageFromCache);
|
||||
}];
|
||||
if (_cacherSupportsNewProtocol) {
|
||||
[_cache cachedImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() completion:^(UIImage *imageFromCache) {
|
||||
completionBlock(imageFromCache);
|
||||
}];
|
||||
} else {
|
||||
[_cache fetchCachedImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() completion:^(CGImageRef coreGraphicsImageFromCache) {
|
||||
UIImage *imageFromCache = (coreGraphicsImageFromCache ? [UIImage imageWithCGImage:coreGraphicsImageFromCache] : nil);
|
||||
completionBlock(imageFromCache);
|
||||
}];
|
||||
}
|
||||
}
|
||||
// If we don't have a cache, just fail immediately.
|
||||
else {
|
||||
@@ -655,22 +734,46 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
}
|
||||
|
||||
// Download!
|
||||
[self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgressBlock:downloadProgressBlock
|
||||
completion:^(CGImageRef coreGraphicsImage, NSError *error) {
|
||||
// We dereference iVars directly, so we can't have weakSelf going nil on us.
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return;
|
||||
|
||||
UIImage *downloadedImage = (coreGraphicsImage ? [UIImage imageWithCGImage:coreGraphicsImage] : nil);
|
||||
completionBlock(downloadedImage, error);
|
||||
|
||||
// Delegateify.
|
||||
if (strongSelf->_delegateFlags.downloadFinish)
|
||||
[strongSelf->_delegate multiplexImageNode:weakSelf didFinishDownloadingImageWithIdentifier:imageIdentifier error:error];
|
||||
}]];
|
||||
if (_downloaderSupportsNewProtocol) {
|
||||
[self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgress:downloadProgressBlock
|
||||
completion:^(UIImage *downloadedImage, NSError *error, id downloadIdentifier) {
|
||||
// We dereference iVars directly, so we can't have weakSelf going nil on us.
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return;
|
||||
|
||||
ASDN::MutexLocker l(_downloadIdentifierLock);
|
||||
//Getting a result back for a different download identifier, download must not have been successfully canceled
|
||||
if (![_downloadIdentifier isEqual:downloadIdentifier] && downloadIdentifier != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
completionBlock(downloadedImage, error);
|
||||
|
||||
// Delegateify.
|
||||
if (strongSelf->_delegateFlags.downloadFinish)
|
||||
[strongSelf->_delegate multiplexImageNode:weakSelf didFinishDownloadingImageWithIdentifier:imageIdentifier error:error];
|
||||
}]];
|
||||
} else {
|
||||
[self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgressBlock:downloadProgressBlock
|
||||
completion:^(CGImageRef coreGraphicsImage, NSError *error) {
|
||||
// We dereference iVars directly, so we can't have weakSelf going nil on us.
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return;
|
||||
|
||||
UIImage *downloadedImage = (coreGraphicsImage ? [UIImage imageWithCGImage:coreGraphicsImage] : nil);
|
||||
completionBlock(downloadedImage, error);
|
||||
|
||||
// Delegateify.
|
||||
if (strongSelf->_delegateFlags.downloadFinish)
|
||||
[strongSelf->_delegate multiplexImageNode:weakSelf didFinishDownloadingImageWithIdentifier:imageIdentifier error:error];
|
||||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -45,19 +45,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* The delegate, which must conform to the <ASNetworkImageNodeDelegate> protocol.
|
||||
*/
|
||||
@property (atomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
|
||||
@property (nonatomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
|
||||
|
||||
/**
|
||||
* A placeholder image to display while the URL is loading.
|
||||
*/
|
||||
@property (nullable, atomic, strong, readwrite) UIImage *defaultImage;
|
||||
@property (nullable, nonatomic, strong, readwrite) UIImage *defaultImage;
|
||||
|
||||
/**
|
||||
* The URL of a new image to download and display.
|
||||
*
|
||||
* @discussion Changing this property will reset the displayed image to a placeholder (<defaultImage>) while loading.
|
||||
*/
|
||||
@property (nullable, atomic, strong, readwrite) NSURL *URL;
|
||||
@property (nullable, nonatomic, strong, readwrite) NSURL *URL;
|
||||
|
||||
/**
|
||||
* Download and display a new image.
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASThread.h"
|
||||
|
||||
#if PIN_REMOTE_IMAGE
|
||||
#import "ASPINRemoteImageDownloader.h"
|
||||
#endif
|
||||
|
||||
@interface ASNetworkImageNode ()
|
||||
{
|
||||
ASDN::RecursiveMutex _lock;
|
||||
@@ -27,9 +31,15 @@
|
||||
UIImage *_defaultImage;
|
||||
|
||||
NSUUID *_cacheUUID;
|
||||
id _imageDownload;
|
||||
id _downloadIdentifier;
|
||||
|
||||
BOOL _imageLoaded;
|
||||
|
||||
//set on init only
|
||||
BOOL _downloaderSupportsNewProtocol;
|
||||
BOOL _downloaderImplementsSetProgress;
|
||||
BOOL _downloaderImplementsSetPriority;
|
||||
BOOL _cacherSupportsNewProtocol;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -42,6 +52,18 @@
|
||||
|
||||
_cache = cache;
|
||||
_downloader = downloader;
|
||||
|
||||
NSAssert([downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgress:completion:)] || [downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:)], @"downloader must respond to either downloadImageWithURL:callbackQueue:downloadProgress:completion: or downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:.");
|
||||
|
||||
_downloaderSupportsNewProtocol = [downloader respondsToSelector:@selector(downloadImageWithURL:callbackQueue:downloadProgress:completion:)] ? YES : NO;
|
||||
|
||||
NSAssert([cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)] || [cache respondsToSelector:@selector(fetchCachedImageWithURL:callbackQueue:completion:)], @"cacher must respond to either cachedImageWithURL:callbackQueue:completion: or fetchCachedImageWithURL:callbackQueue:completion:");
|
||||
|
||||
_downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)] ? YES : NO;
|
||||
_downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)] ? YES : NO;
|
||||
|
||||
_cacherSupportsNewProtocol = [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)] ? YES : NO;
|
||||
|
||||
_shouldCacheImage = YES;
|
||||
self.shouldBypassEnsureDisplay = YES;
|
||||
|
||||
@@ -50,7 +72,11 @@
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
#if PIN_REMOTE_IMAGE
|
||||
return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]];
|
||||
#else
|
||||
return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
@@ -129,6 +155,41 @@
|
||||
[super displayWillStart];
|
||||
|
||||
[self fetchData];
|
||||
|
||||
if (self.image == nil) {
|
||||
if (_downloaderImplementsSetPriority) {
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
if (_downloadIdentifier != nil) {
|
||||
[_downloader setPriority:ASImageDownloaderPriorityDisplay withDownloadIdentifier:_downloadIdentifier];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_downloaderImplementsSetProgress) {
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
|
||||
if (_downloadIdentifier != nil) {
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
[_downloader setProgressImageBlock:^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASDN::MutexLocker l(_lock);
|
||||
//Getting a result back for a different download identifier, download must not have been successfully canceled
|
||||
if (![strongSelf->_downloadIdentifier isEqual:downloadIdentifier] && downloadIdentifier != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
strongSelf.image = progressImage;
|
||||
} callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
@@ -158,31 +219,44 @@
|
||||
|
||||
- (void)_cancelImageDownload
|
||||
{
|
||||
if (!_imageDownload) {
|
||||
if (!_downloadIdentifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_downloader cancelImageDownloadForIdentifier:_imageDownload];
|
||||
_imageDownload = nil;
|
||||
if (_downloadIdentifier) {
|
||||
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
|
||||
}
|
||||
_downloadIdentifier = nil;
|
||||
|
||||
_cacheUUID = nil;
|
||||
}
|
||||
|
||||
- (void)_downloadImageWithCompletion:(void (^)(CGImageRef, NSError*))finished
|
||||
- (void)_downloadImageWithCompletion:(void (^)(UIImage *image, NSError*, id downloadIdentifier))finished
|
||||
{
|
||||
_imageDownload = [_downloader downloadImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgressBlock:NULL
|
||||
completion:^(CGImageRef responseImage, NSError *error) {
|
||||
if (finished != NULL) {
|
||||
finished(responseImage, error);
|
||||
}
|
||||
}];
|
||||
if (_downloaderSupportsNewProtocol) {
|
||||
_downloadIdentifier = [_downloader downloadImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgress:NULL
|
||||
completion:^(UIImage * _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
||||
if (finished != NULL) {
|
||||
finished(image, error, downloadIdentifier);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
_downloadIdentifier = [_downloader downloadImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
downloadProgressBlock:NULL
|
||||
completion:^(CGImageRef responseImage, NSError *error) {
|
||||
if (finished != NULL) {
|
||||
finished([UIImage imageWithCGImage:responseImage], error, nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_lazilyLoadImageIfNecessary
|
||||
{
|
||||
if (!_imageLoaded && _URL != nil && _imageDownload == nil) {
|
||||
if (!_imageLoaded && _URL != nil && _downloadIdentifier == nil) {
|
||||
if (_URL.isFileURL) {
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
@@ -210,7 +284,7 @@
|
||||
}
|
||||
} else {
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
void (^finished)(CGImageRef, NSError *) = ^(CGImageRef responseImage, NSError *error) {
|
||||
void (^finished)(UIImage *, NSError *, id downloadIdentifier) = ^(UIImage *responseImage, NSError *error, id downloadIdentifier) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
@@ -218,13 +292,18 @@
|
||||
|
||||
{
|
||||
ASDN::MutexLocker l(strongSelf->_lock);
|
||||
|
||||
//Getting a result back for a different download identifier, download must not have been successfully canceled
|
||||
if (![strongSelf->_downloadIdentifier isEqual:downloadIdentifier] && downloadIdentifier != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseImage != NULL) {
|
||||
strongSelf->_imageLoaded = YES;
|
||||
strongSelf.image = [UIImage imageWithCGImage:responseImage];
|
||||
strongSelf.image = responseImage;
|
||||
}
|
||||
|
||||
strongSelf->_imageDownload = nil;
|
||||
strongSelf->_downloadIdentifier = nil;
|
||||
|
||||
strongSelf->_cacheUUID = nil;
|
||||
}
|
||||
@@ -241,22 +320,32 @@
|
||||
NSUUID *cacheUUID = [NSUUID UUID];
|
||||
_cacheUUID = cacheUUID;
|
||||
|
||||
void (^cacheCompletion)(CGImageRef) = ^(CGImageRef image) {
|
||||
void (^cacheCompletion)(UIImage *) = ^(UIImage *image) {
|
||||
// If the cache UUID changed, that means this request was cancelled.
|
||||
if (![_cacheUUID isEqual:cacheUUID]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (image == NULL && _downloader != nil) {
|
||||
[self _downloadImageWithCompletion:finished];
|
||||
} else {
|
||||
finished(image, NULL);
|
||||
finished(image, NULL, nil);
|
||||
}
|
||||
};
|
||||
|
||||
[_cache fetchCachedImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
completion:cacheCompletion];
|
||||
|
||||
if (_cacherSupportsNewProtocol) {
|
||||
[_cache cachedImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
completion:cacheCompletion];
|
||||
} else {
|
||||
void (^oldCacheCompletion)(CGImageRef) = ^(CGImageRef image) {
|
||||
cacheCompletion([UIImage imageWithCGImage:image]);
|
||||
};
|
||||
|
||||
[_cache fetchCachedImageWithURL:_URL
|
||||
callbackQueue:dispatch_get_main_queue()
|
||||
completion:oldCacheCompletion];
|
||||
}
|
||||
} else {
|
||||
[self _downloadImageWithCompletion:finished];
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
|
||||
|
||||
- (id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
downloadProgressBlock:(void (^)(CGFloat))downloadProgressBlock
|
||||
downloadProgress:(void (^)(CGFloat))downloadProgressBlock
|
||||
completion:(void (^)(CGImageRef, NSError *))completion
|
||||
{
|
||||
ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL];
|
||||
|
||||
@@ -6,51 +6,51 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^ASImageCacherCompletion)(UIImage * _Nullable imageFromCache);
|
||||
|
||||
@protocol ASImageCacheProtocol <NSObject>
|
||||
|
||||
@required
|
||||
/**
|
||||
@abstract Attempts to fetch an image with the given URL from the cache.
|
||||
@param URL The URL of the image to retrieve from the cache.
|
||||
@param callbackQueue The queue to call `completion` on. If this value is nil, @{ref completion} will be invoked on the
|
||||
main-queue.
|
||||
@param completion The block to be called when the cache has either hit or missed.
|
||||
@param imageFromCache The image that was retrieved from the cache, if the image could be retrieved; nil otherwise.
|
||||
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
|
||||
the calling thread as it is likely to be called from the main thread.
|
||||
@deprecated This method is deprecated @see cachedImageWithURL:callbackQueue:completion: instead
|
||||
*/
|
||||
- (void)fetchCachedImageWithURL:(nullable NSURL *)URL
|
||||
callbackQueue:(nullable dispatch_queue_t)callbackQueue
|
||||
completion:(void (^)(CGImageRef _Nullable imageFromCache))completion;
|
||||
|
||||
/**
|
||||
@abstract Attempts to fetch an image with the given URL from the cache.
|
||||
@param URL The URL of the image to retrieve from the cache.
|
||||
@param callbackQueue The queue to call `completion` on. If this value is nil, @{ref completion} will be invoked on the
|
||||
main-queue.
|
||||
@param completion The block to be called when the cache has either hit or missed.
|
||||
@param imageFromCache The image that was retrieved from the cache, if the image could be retrieved; nil otherwise.
|
||||
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
|
||||
the calling thread as it is likely to be called from the main thread.
|
||||
*/
|
||||
- (void)cachedImageWithURL:(nullable NSURL *)URL
|
||||
callbackQueue:(nullable dispatch_queue_t)callbackQueue
|
||||
completion:(ASImageCacherCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
typedef void(^ASImageDownloaderCompletion)(UIImage * _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier);
|
||||
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
|
||||
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, id _Nullable downloadIdentifier);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
|
||||
ASImageDownloaderPriorityNormal = 0,
|
||||
ASImageDownloaderPriorityDisplay,
|
||||
};
|
||||
|
||||
@protocol ASImageDownloaderProtocol <NSObject>
|
||||
|
||||
@required
|
||||
/**
|
||||
@abstract Downloads an image with the given URL.
|
||||
@param URL The URL of the image to download.
|
||||
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on. If this value is nil, both blocks
|
||||
will be invoked on the main-queue.
|
||||
@param downloadProgressBlock The block to be invoked when the download of `URL` progresses.
|
||||
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
||||
@param completion The block to be invoked when the download has completed, or has failed.
|
||||
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
||||
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
|
||||
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
||||
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
||||
retain the identifier if you wish to use it later.
|
||||
*/
|
||||
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(nullable dispatch_queue_t)callbackQueue
|
||||
downloadProgressBlock:(void (^ _Nullable)(CGFloat progress))downloadProgressBlock
|
||||
completion:(void (^ _Nullable)(CGImageRef _Nullable image, NSError * _Nullable error))completion;
|
||||
|
||||
/**
|
||||
@abstract Cancels an image download.
|
||||
@@ -58,7 +58,58 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
|
||||
@discussion This method has no effect if `downloadIdentifier` is nil.
|
||||
*/
|
||||
- (void)cancelImageDownloadForIdentifier:(nullable id)downloadIdentifier;
|
||||
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier;
|
||||
|
||||
@optional
|
||||
|
||||
//You must implement one of the two following methods
|
||||
|
||||
/**
|
||||
@deprecated This method is deprecated @see downloadImageWithURL:callbackQueue:downloadProgress:completion: instead
|
||||
*/
|
||||
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(nullable dispatch_queue_t)callbackQueue
|
||||
downloadProgressBlock:(void (^ _Nullable)(CGFloat progress))downloadProgressBlock
|
||||
completion:(void (^ _Nullable)(CGImageRef _Nullable image, NSError * _Nullable error))completion;
|
||||
|
||||
/**
|
||||
@abstract Downloads an image with the given URL.
|
||||
@param URL The URL of the image to download.
|
||||
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
|
||||
@param downloadProgress The block to be invoked when the download of `URL` progresses.
|
||||
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
||||
@param completion The block to be invoked when the download has completed, or has failed.
|
||||
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
||||
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
|
||||
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
||||
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
||||
retain the identifier if you wish to use it later.
|
||||
*/
|
||||
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
||||
completion:(nullable ASImageDownloaderCompletion)completion;
|
||||
|
||||
|
||||
/**
|
||||
@abstract Sets block to be called when a progress image is available.
|
||||
@param progressBlock The block to be invoked when the download has a progressive render of an image available.
|
||||
@param callbackQueue The queue to call `progressBlock` on.
|
||||
@param downloadIdentifier The opaque download identifier object returned from
|
||||
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
|
||||
*/
|
||||
- (void)setProgressImageBlock:(nullable ASImageDownloaderProgressImage)progressBlock
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
withDownloadIdentifier:(id)downloadIdentifier;
|
||||
|
||||
/**
|
||||
@abstract Called to indicate what priority an image should be downloaded at.
|
||||
@param priority The priority at which the image should be downloaded.
|
||||
@param downloadIdentifier The opaque download identifier object returned from
|
||||
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
|
||||
*/
|
||||
- (void)setPriority:(ASImageDownloaderPriority)priority
|
||||
withDownloadIdentifier:(id)downloadIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
16
AsyncDisplayKit/Details/ASPINRemoteImageDownloader.h
Normal file
16
AsyncDisplayKit/Details/ASPINRemoteImageDownloader.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// ASPINRemoteImageDownloader.h
|
||||
// Pods
|
||||
//
|
||||
// Created by Garrett Moon on 2/5/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASImageProtocols.h"
|
||||
|
||||
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
|
||||
|
||||
+ (instancetype)sharedDownloader;
|
||||
|
||||
@end
|
||||
97
AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m
Normal file
97
AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// ASPINRemoteImageDownloader.m
|
||||
// Pods
|
||||
//
|
||||
// Created by Garrett Moon on 2/5/16.
|
||||
//
|
||||
//
|
||||
|
||||
#ifdef PIN_REMOTE_IMAGE
|
||||
#import "ASPINRemoteImageDownloader.h"
|
||||
#import <PINRemoteImage/PINRemoteImageManager.h>
|
||||
#import <PINCache/PINCache.h>
|
||||
|
||||
@implementation ASPINRemoteImageDownloader
|
||||
|
||||
+ (instancetype)sharedDownloader
|
||||
{
|
||||
static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once(&once, ^{
|
||||
sharedDownloader = [[ASPINRemoteImageDownloader alloc] init];
|
||||
});
|
||||
return sharedDownloader;
|
||||
}
|
||||
|
||||
#pragma mark ASImageProtocols
|
||||
|
||||
- (void)fetchCachedImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
completion:(void (^)(CGImageRef imageFromCache))completion
|
||||
{
|
||||
NSString *key = [[PINRemoteImageManager sharedImageManager] cacheKeyForURL:URL processorKey:nil];
|
||||
UIImage *image = [[[[PINRemoteImageManager sharedImageManager] cache] memoryCache] objectForKey:key];
|
||||
|
||||
dispatch_async(callbackQueue, ^{
|
||||
completion([image CGImage]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@abstract Downloads an image with the given URL.
|
||||
@param URL The URL of the image to download.
|
||||
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on. If this value is nil, both blocks
|
||||
will be invoked on the main-queue.
|
||||
@param downloadProgressBlock The block to be invoked when the download of `URL` progresses.
|
||||
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
||||
@param completion The block to be invoked when the download has completed, or has failed.
|
||||
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
||||
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
|
||||
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
||||
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
||||
retain the identifier if you wish to use it later.
|
||||
*/
|
||||
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
downloadProgressBlock:(void (^)(CGFloat progress))downloadProgressBlock
|
||||
completion:(void (^)(CGImageRef image, NSError * error, id downloadIdentifier))completion
|
||||
{
|
||||
return [[PINRemoteImageManager sharedImageManager] downloadImageWithURL:URL completion:^(PINRemoteImageManagerResult *result) {
|
||||
dispatch_async(callbackQueue, ^{
|
||||
completion([result.image CGImage], result.error, result.UUID);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
@abstract Cancels an image download.
|
||||
@param downloadIdentifier The opaque download identifier object returned from
|
||||
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
|
||||
@discussion This method has no effect if `downloadIdentifier` is nil.
|
||||
*/
|
||||
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
NSAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID");
|
||||
[[PINRemoteImageManager sharedImageManager] cancelTaskWithUUID:downloadIdentifier];
|
||||
}
|
||||
|
||||
- (void)setProgressImageBlock:(ASImageDownloaderProgressImage)progressBlock callbackQueue:(dispatch_queue_t)callbackQueue withDownloadIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
NSAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID");
|
||||
|
||||
[[PINRemoteImageManager sharedImageManager] setProgressCallback:^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||
dispatch_async(callbackQueue, ^{
|
||||
progressBlock(result.image, result.UUID);
|
||||
});
|
||||
} ofTaskWithUUID:downloadIdentifier];
|
||||
}
|
||||
|
||||
- (void)setPriority:(ASImageDownloaderPriority)priority withDownloadIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
NSAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID");
|
||||
|
||||
[[PINRemoteImageManager sharedImageManager] setPriority:PINRemoteImageManagerPriorityHigh ofTaskWithUUID:downloadIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
@@ -145,14 +145,14 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)())
|
||||
NSArray *URL;
|
||||
[inv getArgument:&URL atIndex:2];
|
||||
|
||||
void (^completionBlock)(CGImageRef);
|
||||
ASImageCacherCompletion completionBlock;
|
||||
[inv getArgument:&completionBlock atIndex:4];
|
||||
|
||||
// Call the completion block with our test image and URL.
|
||||
NSURL *testImageURL = [self _testImageURL];
|
||||
XCTAssertEqualObjects(URL, testImageURL, @"Fetching URL other than test image");
|
||||
completionBlock([self _testImage].CGImage);
|
||||
}] fetchCachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]];
|
||||
completionBlock([self _testImage]);
|
||||
}] cachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]];
|
||||
|
||||
imageNode.imageIdentifiers = @[imageIdentifier];
|
||||
// Kick off loading.
|
||||
@@ -302,25 +302,25 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)())
|
||||
// Mock a cache miss.
|
||||
id mockCache = [OCMockObject mockForProtocol:@protocol(ASImageCacheProtocol)];
|
||||
[[[mockCache stub] andDo:^(NSInvocation *inv) {
|
||||
void (^completion)(CGImageRef imageFromCache);
|
||||
ASImageCacherCompletion completion;
|
||||
[inv getArgument:&completion atIndex:4];
|
||||
completion(nil);
|
||||
}] fetchCachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]];
|
||||
}] cachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]];
|
||||
|
||||
// Mock a 50%-progress URL download.
|
||||
id mockDownloader = [OCMockObject mockForProtocol:@protocol(ASImageDownloaderProtocol)];
|
||||
const CGFloat mockedProgress = 0.5;
|
||||
[[[mockDownloader stub] andDo:^(NSInvocation *inv) {
|
||||
// Simulate progress.
|
||||
void (^progressBlock)(CGFloat progress);
|
||||
ASImageDownloaderProgress progressBlock;
|
||||
[inv getArgument:&progressBlock atIndex:4];
|
||||
progressBlock(mockedProgress);
|
||||
|
||||
// Simulate completion.
|
||||
void (^completionBlock)(CGImageRef image, NSError *error);
|
||||
ASImageDownloaderCompletion completionBlock;
|
||||
[inv getArgument:&completionBlock atIndex:5];
|
||||
completionBlock([self _testImage].CGImage, nil);
|
||||
}] downloadImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] downloadProgressBlock:[OCMArg any] completion:[OCMArg any]];
|
||||
completionBlock([self _testImage], nil, nil);
|
||||
}] downloadImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] downloadProgress:[OCMArg any] completion:[OCMArg any]];
|
||||
|
||||
ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:mockCache downloader:mockDownloader];
|
||||
NSNumber *imageIdentifier = @1;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
// multiplex image node!
|
||||
// NB: we're using a custom downloader with an artificial delay for this demo, but ASBasicImageDownloader works too!
|
||||
// NB: we're using a custom downloader with an artificial delay for this demo, but ASPINRemoteImageDownloader works too!
|
||||
_imageNode = [[ASMultiplexImageNode alloc] initWithCache:nil downloader:self];
|
||||
_imageNode.dataSource = self;
|
||||
_imageNode.delegate = self;
|
||||
|
||||
Reference in New Issue
Block a user