mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
Have ASNetworkImageNode report whether images were cached or not (#639)
* Have ASNetworkImageNode report whether images were cached or not. * Update changelog * Add fileURL case
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
- [ASCollectionView] Improve index space translation of Flow Layout Delegate methods. [Scott Goodson](https://github.com/appleguy)
|
- [ASCollectionView] Improve index space translation of Flow Layout Delegate methods. [Scott Goodson](https://github.com/appleguy)
|
||||||
- [Animated Image] Adds support for animated WebP as well as improves GIF handling. [#605](https://github.com/TextureGroup/Texture/pull/605) [Garrett Moon](https://github.com/garrettmoon)
|
- [Animated Image] Adds support for animated WebP as well as improves GIF handling. [#605](https://github.com/TextureGroup/Texture/pull/605) [Garrett Moon](https://github.com/garrettmoon)
|
||||||
- [ASCollectionView] Check if batch fetching is needed if batch fetching parameter has been changed. [#624](https://github.com/TextureGroup/Texture/pull/624) [Garrett Moon](https://github.com/garrettmoon)
|
- [ASCollectionView] Check if batch fetching is needed if batch fetching parameter has been changed. [#624](https://github.com/TextureGroup/Texture/pull/624) [Garrett Moon](https://github.com/garrettmoon)
|
||||||
|
- [ASNetworkImageNode] New delegate callback to tell the consumer whether the image was loaded from cache or download. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
|
||||||
## 2.6
|
## 2.6
|
||||||
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
|
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
|
||||||
|
|||||||
@@ -130,6 +130,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, ASNetworkImageSource) {
|
||||||
|
ASNetworkImageSourceUnspecified = 0,
|
||||||
|
ASNetworkImageSourceSynchronousCache,
|
||||||
|
ASNetworkImageSourceAsynchronousCache,
|
||||||
|
ASNetworkImageSourceFileURL,
|
||||||
|
ASNetworkImageSourceDownload,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A struct that carries details about ASNetworkImageNode's image loads.
|
||||||
|
typedef struct {
|
||||||
|
/// The source from which the image was loaded.
|
||||||
|
ASNetworkImageSource imageSource;
|
||||||
|
} ASNetworkImageNodeDidLoadInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to
|
* The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to
|
||||||
* notifications such as finished decoding and downloading an image.
|
* notifications such as finished decoding and downloading an image.
|
||||||
@@ -137,6 +152,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@protocol ASNetworkImageNodeDelegate <NSObject>
|
@protocol ASNetworkImageNodeDelegate <NSObject>
|
||||||
@optional
|
@optional
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification that the image node finished downloading an image, with additional info.
|
||||||
|
* If implemented, this method will be called instead of `imageNode:didLoadImage:`.
|
||||||
|
*
|
||||||
|
* @param imageNode The sender.
|
||||||
|
* @param image The newly-loaded image.
|
||||||
|
* @param info Misc information about the image load.
|
||||||
|
*
|
||||||
|
* @discussion Called on a background queue.
|
||||||
|
*/
|
||||||
|
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageNodeDidLoadInfo)info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that the image node finished downloading an image.
|
* Notification that the image node finished downloading an image.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
unsigned int delegateDidFailWithError:1;
|
unsigned int delegateDidFailWithError:1;
|
||||||
unsigned int delegateDidFinishDecoding:1;
|
unsigned int delegateDidFinishDecoding:1;
|
||||||
unsigned int delegateDidLoadImage:1;
|
unsigned int delegateDidLoadImage:1;
|
||||||
|
unsigned int delegateDidLoadImageWithInfo:1;
|
||||||
} _delegateFlags;
|
} _delegateFlags;
|
||||||
|
|
||||||
|
|
||||||
@@ -305,6 +306,7 @@
|
|||||||
_delegateFlags.delegateDidFailWithError = [delegate respondsToSelector:@selector(imageNode:didFailWithError:)];
|
_delegateFlags.delegateDidFailWithError = [delegate respondsToSelector:@selector(imageNode:didFailWithError:)];
|
||||||
_delegateFlags.delegateDidFinishDecoding = [delegate respondsToSelector:@selector(imageNodeDidFinishDecoding:)];
|
_delegateFlags.delegateDidFinishDecoding = [delegate respondsToSelector:@selector(imageNodeDidFinishDecoding:)];
|
||||||
_delegateFlags.delegateDidLoadImage = [delegate respondsToSelector:@selector(imageNode:didLoadImage:)];
|
_delegateFlags.delegateDidLoadImage = [delegate respondsToSelector:@selector(imageNode:didLoadImage:)];
|
||||||
|
_delegateFlags.delegateDidLoadImageWithInfo = [delegate respondsToSelector:@selector(imageNode:didLoadImage:info:)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<ASNetworkImageNodeDelegate>)delegate
|
- (id<ASNetworkImageNodeDelegate>)delegate
|
||||||
@@ -353,8 +355,18 @@
|
|||||||
if (result) {
|
if (result) {
|
||||||
[self _locked_setCurrentImageQuality:1.0];
|
[self _locked_setCurrentImageQuality:1.0];
|
||||||
[self _locked__setImage:result];
|
[self _locked__setImage:result];
|
||||||
|
|
||||||
_imageLoaded = YES;
|
_imageLoaded = YES;
|
||||||
|
|
||||||
|
// Call out to the delegate.
|
||||||
|
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
||||||
|
ASDN::MutexUnlocker l(__instanceLock__);
|
||||||
|
ASNetworkImageNodeDidLoadInfo info = {};
|
||||||
|
info.imageSource = ASNetworkImageSourceSynchronousCache;
|
||||||
|
[_delegate imageNode:self didLoadImage:result info:info];
|
||||||
|
} else if (_delegateFlags.delegateDidLoadImage) {
|
||||||
|
ASDN::MutexUnlocker l(__instanceLock__);
|
||||||
|
[_delegate imageNode:self didLoadImage:result];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -688,14 +700,19 @@
|
|||||||
|
|
||||||
[self _locked_setCurrentImageQuality:1.0];
|
[self _locked_setCurrentImageQuality:1.0];
|
||||||
|
|
||||||
if (_delegateFlags.delegateDidLoadImage) {
|
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
||||||
|
ASDN::MutexUnlocker u(__instanceLock__);
|
||||||
|
ASNetworkImageNodeDidLoadInfo info = {};
|
||||||
|
info.imageSource = ASNetworkImageSourceFileURL;
|
||||||
|
[delegate imageNode:self didLoadImage:self.image info:info];
|
||||||
|
} else if (_delegateFlags.delegateDidLoadImage) {
|
||||||
ASDN::MutexUnlocker u(__instanceLock__);
|
ASDN::MutexUnlocker u(__instanceLock__);
|
||||||
[delegate imageNode:self didLoadImage:self.image];
|
[delegate imageNode:self didLoadImage:self.image];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
__weak __typeof__(self) weakSelf = self;
|
__weak __typeof__(self) weakSelf = self;
|
||||||
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier) {
|
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSource imageSource) {
|
||||||
|
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
__typeof__(self) strongSelf = weakSelf;
|
||||||
if (strongSelf == nil) {
|
if (strongSelf == nil) {
|
||||||
@@ -732,7 +749,12 @@
|
|||||||
strongSelf->_cacheUUID = nil;
|
strongSelf->_cacheUUID = nil;
|
||||||
|
|
||||||
if (imageContainer != nil) {
|
if (imageContainer != nil) {
|
||||||
if (strongSelf->_delegateFlags.delegateDidLoadImage) {
|
if (strongSelf->_delegateFlags.delegateDidLoadImageWithInfo) {
|
||||||
|
ASDN::MutexUnlocker u(strongSelf->__instanceLock__);
|
||||||
|
ASNetworkImageNodeDidLoadInfo info = {};
|
||||||
|
info.imageSource = imageSource;
|
||||||
|
[delegate imageNode:strongSelf didLoadImage:strongSelf.image info:info];
|
||||||
|
} else if (strongSelf->_delegateFlags.delegateDidLoadImage) {
|
||||||
ASDN::MutexUnlocker u(strongSelf->__instanceLock__);
|
ASDN::MutexUnlocker u(strongSelf->__instanceLock__);
|
||||||
[delegate imageNode:strongSelf didLoadImage:strongSelf.image];
|
[delegate imageNode:strongSelf didLoadImage:strongSelf.image];
|
||||||
}
|
}
|
||||||
@@ -763,10 +785,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ([imageContainer asdk_image] == nil && _downloader != nil) {
|
if ([imageContainer asdk_image] == nil && _downloader != nil) {
|
||||||
[self _downloadImageWithCompletion:finished];
|
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
|
||||||
|
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
|
||||||
|
}];
|
||||||
} else {
|
} else {
|
||||||
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
|
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
|
||||||
finished(imageContainer, nil, nil);
|
finished(imageContainer, nil, nil, ASNetworkImageSourceAsynchronousCache);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -780,7 +804,9 @@
|
|||||||
completion:completion];
|
completion:completion];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
[self _downloadImageWithCompletion:finished];
|
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
|
||||||
|
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,15 +202,11 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
completion:(ASImageCacherCompletion)completion
|
completion:(ASImageCacherCompletion)completion
|
||||||
{
|
{
|
||||||
// We do not check the cache here and instead check it in downloadImageWithURL to avoid checking the cache twice.
|
[[self sharedPINRemoteImageManager] imageFromCacheWithURL:URL processorKey:nil options:PINRemoteImageManagerDownloadOptionsSkipDecode completion:^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||||
// If we're targeting the main queue and we're on the main thread, complete immediately.
|
[ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
completion(result.image);
|
||||||
completion(nil);
|
}];
|
||||||
} else {
|
}];
|
||||||
dispatch_async(callbackQueue, ^{
|
|
||||||
completion(nil);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
|
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
|
||||||
@@ -259,19 +255,13 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) {
|
PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) {
|
||||||
if (downloadProgress == nil) { return; }
|
if (downloadProgress == nil) { return; }
|
||||||
|
|
||||||
/// If we're targeting the main queue and we're on the main thread, call immediately.
|
[ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
|
||||||
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
||||||
} else {
|
}];
|
||||||
dispatch_async(callbackQueue, ^{
|
|
||||||
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PINRemoteImageManagerImageCompletion imageCompletion = ^(PINRemoteImageManagerResult * _Nonnull result) {
|
PINRemoteImageManagerImageCompletion imageCompletion = ^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||||
/// If we're targeting the main queue and we're on the main thread, complete immediately.
|
[ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
if (result.alternativeRepresentation) {
|
if (result.alternativeRepresentation) {
|
||||||
completion(result.alternativeRepresentation, result.error, result.UUID);
|
completion(result.alternativeRepresentation, result.error, result.UUID);
|
||||||
@@ -281,23 +271,16 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
#else
|
#else
|
||||||
completion(result.image, result.error, result.UUID);
|
completion(result.image, result.error, result.UUID);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
}];
|
||||||
dispatch_async(callbackQueue, ^{
|
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
|
||||||
if (result.alternativeRepresentation) {
|
|
||||||
completion(result.alternativeRepresentation, result.error, result.UUID);
|
|
||||||
} else {
|
|
||||||
completion(result.image, result.error, result.UUID);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
completion(result.image, result.error, result.UUID);
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// add "IgnoreCache" option since we have a caching API so we already checked it, not worth checking again.
|
||||||
|
// PINRemoteImage is responsible for coalescing downloads, and even if it wasn't, the tiny probability of
|
||||||
|
// extra downloads isn't worth the effort of rechecking caches every single time. In order to provide
|
||||||
|
// feedback to the consumer about whether images are cached, we can't simply make the cache a no-op and
|
||||||
|
// check the cache as part of this download.
|
||||||
return [[self sharedPINRemoteImageManager] downloadImageWithURLs:URLs
|
return [[self sharedPINRemoteImageManager] downloadImageWithURLs:URLs
|
||||||
options:PINRemoteImageManagerDownloadOptionsSkipDecode
|
options:PINRemoteImageManagerDownloadOptionsSkipDecode | PINRemoteImageManagerDownloadOptionsIgnoreCache
|
||||||
progressImage:nil
|
progressImage:nil
|
||||||
progressDownload:progressDownload
|
progressDownload:progressDownload
|
||||||
completion:imageCompletion];
|
completion:imageCompletion];
|
||||||
@@ -369,5 +352,29 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If on main thread and queue is main, perform now.
|
||||||
|
* If queue is nil, assert and perform now.
|
||||||
|
* Otherwise, dispatch async to queue.
|
||||||
|
*/
|
||||||
|
+ (void)_performWithCallbackQueue:(dispatch_queue_t)queue work:(void (^)())work
|
||||||
|
{
|
||||||
|
if (work == nil) {
|
||||||
|
// No need to assert here, really. We aren't expecting any feedback from this method.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASDisplayNodeThreadIsMain() && queue == dispatch_get_main_queue()) {
|
||||||
|
work();
|
||||||
|
} else if (queue == nil) {
|
||||||
|
ASDisplayNodeFailAssert(@"Callback queue should not be nil.");
|
||||||
|
work();
|
||||||
|
} else {
|
||||||
|
dispatch_async(queue, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user