From fef965f78ef5424b8dae80cfeb29cd829ad62f4a Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Tue, 30 Jan 2018 17:50:38 -0500 Subject: [PATCH] Add support for providing additional info to network image node delegate (#775) * Add support for piping arbitrary user info from ASImageDownloader to the ASNetworkImageNodeDelegate * s/source/sourceType * Fix stuff and take Michael's advice --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 +++++ CHANGELOG.md | 3 ++ Source/ASMultiplexImageNode.mm | 2 +- Source/ASNetworkImageLoadInfo.h | 39 ++++++++++++++++ Source/ASNetworkImageLoadInfo.m | 32 ++++++++++++++ Source/ASNetworkImageNode.h | 19 ++------ Source/ASNetworkImageNode.mm | 44 ++++++++++--------- Source/AsyncDisplayKit.h | 1 + Source/Details/ASBasicImageDownloader.h | 5 ++- Source/Details/ASBasicImageDownloader.mm | 10 ++--- Source/Details/ASImageProtocols.h | 3 +- Source/Details/ASPINRemoteImageDownloader.h | 5 ++- Source/Details/ASPINRemoteImageDownloader.m | 8 ++-- .../Private/ASNetworkImageLoadInfo+Private.h | 22 ++++++++++ Tests/ASBasicImageDownloaderTests.m | 4 +- Tests/ASMultiplexImageNodeTests.m | 2 +- examples/ASDKgram/Sample/PhotoCellNode.m | 17 +++++++ 17 files changed, 173 insertions(+), 55 deletions(-) create mode 100644 Source/ASNetworkImageLoadInfo.h create mode 100644 Source/ASNetworkImageLoadInfo.m create mode 100644 Source/Private/ASNetworkImageLoadInfo+Private.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 1cff2cac0..19771c9ea 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -410,6 +410,9 @@ CCE4F9B51F0DA4F300062E4E /* ASLayoutEngineTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B41F0DA4F300062E4E /* ASLayoutEngineTests.mm */; }; CCE4F9BA1F0DBB5000062E4E /* ASLayoutTestNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B71F0DBA5000062E4E /* ASLayoutTestNode.mm */; }; CCE4F9BE1F0ECE5200062E4E /* ASTLayoutFixture.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */; }; + CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */; }; + CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; }; DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; @@ -913,6 +916,9 @@ CCE4F9BB1F0EA67F00062E4E /* debugbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debugbreak.h; sourceTree = ""; }; CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTLayoutFixture.h; sourceTree = ""; }; CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTLayoutFixture.mm; sourceTree = ""; }; + CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASNetworkImageLoadInfo.h; sourceTree = ""; }; + CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageLoadInfo.m; sourceTree = ""; }; + CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASNetworkImageLoadInfo+Private.h"; sourceTree = ""; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = ""; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = ""; }; D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = ""; }; @@ -1074,6 +1080,8 @@ 058D09B1195D04C000B7D73C /* Source */ = { isa = PBXGroup; children = ( + CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */, + CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */, CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */, CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */, DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */, @@ -1376,6 +1384,7 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */, CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */, CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */, CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */, @@ -1913,6 +1922,7 @@ DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */, CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */, 254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */, + CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */, 9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1964,6 +1974,7 @@ B35062391B010EFD0018CF92 /* ASThread.h in Headers */, 2C107F5B1BA9F54500F13DE5 /* AsyncDisplayKit.h in Headers */, 509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */, + CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */, B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */, 044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, @@ -2292,6 +2303,7 @@ E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */, 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */, CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */, + CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */, 68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */, E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */, 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */, diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1f2475a..55954a8fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ - [ASCollectionNode] Added support for interactive item movement. [Adlai Holler](https://github.com/Adlai-Holler) - Added an experimental "no-copy" rendering API. See ASGraphicsContext.h for info. [Adlai Holler](https://github.com/Adlai-Holler) - Dropped support for iOS 8. [Adlai Holler](https://github.com/Adlai-Holler) +- **Breaking** Changes to ASNetworkImageNode: [Adlai Holler](https://github.com/Adlai-Holler) + - Modified `ASImageDownloaderCompletion` to add an optional `id userInfo` field. Your custom downloader can pass `nil`. + - Modified the last argument to `-[ASNetworkImageNodeDelegate imageNode:didLoadImage:info:]` method from a struct to an object of new class `ASNetworkImageLoadInfo` which includes other metadata about the load operation. - Removed +load static initializer from ASDisplayNode. [Adlai Holler](https://github.com/Adlai-Holler) ## 2.6 diff --git a/Source/ASMultiplexImageNode.mm b/Source/ASMultiplexImageNode.mm index a2bed044a..ecc5fd7af 100644 --- a/Source/ASMultiplexImageNode.mm +++ b/Source/ASMultiplexImageNode.mm @@ -823,7 +823,7 @@ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL c [self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() downloadProgress:downloadProgressBlock - completion:^(id imageContainer, NSError *error, id downloadIdentifier) { + completion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { // We dereference iVars directly, so we can't have weakSelf going nil on us. __typeof__(self) strongSelf = weakSelf; if (!strongSelf) diff --git a/Source/ASNetworkImageLoadInfo.h b/Source/ASNetworkImageLoadInfo.h new file mode 100644 index 000000000..51e23e375 --- /dev/null +++ b/Source/ASNetworkImageLoadInfo.h @@ -0,0 +1,39 @@ +// +// ASNetworkImageLoadInfo.h +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, ASNetworkImageSourceType) { + ASNetworkImageSourceUnspecified = 0, + ASNetworkImageSourceSynchronousCache, + ASNetworkImageSourceAsynchronousCache, + ASNetworkImageSourceFileURL, + ASNetworkImageSourceDownload, +}; + +AS_SUBCLASSING_RESTRICTED +@interface ASNetworkImageLoadInfo : NSObject + +/// The type of source from which the image was loaded. +@property (readonly) ASNetworkImageSourceType sourceType; + +/// The image URL that was downloaded. +@property (readonly) NSURL *url; + +/// The download identifier, if one was provided. +@property (nullable, readonly) id downloadIdentifier; + +/// The userInfo object provided by the downloader, if one was provided. +@property (nullable, readonly) id userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASNetworkImageLoadInfo.m b/Source/ASNetworkImageLoadInfo.m new file mode 100644 index 000000000..69dfee760 --- /dev/null +++ b/Source/ASNetworkImageLoadInfo.m @@ -0,0 +1,32 @@ +// +// ASNetworkImageLoadInfo.m +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import +#import + +@implementation ASNetworkImageLoadInfo + +- (instancetype)initWithURL:(NSURL *)url sourceType:(ASNetworkImageSourceType)sourceType downloadIdentifier:(id)downloadIdentifier userInfo:(id)userInfo +{ + if (self = [super init]) { + _url = [url copy]; + _sourceType = sourceType; + _downloadIdentifier = downloadIdentifier; + _userInfo = userInfo; + } + return self; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +@end diff --git a/Source/ASNetworkImageNode.h b/Source/ASNetworkImageNode.h index c4c83214b..4100b5220 100644 --- a/Source/ASNetworkImageNode.h +++ b/Source/ASNetworkImageNode.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol; +@class ASNetworkImageLoadInfo; /** @@ -134,20 +135,6 @@ NS_ASSUME_NONNULL_BEGIN #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 * notifications such as finished decoding and downloading an image. @@ -161,11 +148,11 @@ typedef struct { * * @param imageNode The sender. * @param image The newly-loaded image. - * @param info Misc information about the image load. + * @param info Additional information about the image load. * * @discussion Called on a background queue. */ -- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageNodeDidLoadInfo)info; +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageLoadInfo *)info; /** * Notification that the image node finished downloading an image. diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index 2240e3ae9..c41a02ae7 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -27,6 +27,7 @@ #import #import #import +#import #if AS_PIN_REMOTE_IMAGE #import @@ -334,8 +335,9 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) { ASDN::MutexLocker l(__instanceLock__); - if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) { - UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image]; + NSURL *url = _URL; + if (_imageLoaded == NO && url && _downloadIdentifier == nil) { + UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image]; if (result) { [self _locked_setCurrentImageQuality:1.0]; [self _locked__setImage:result]; @@ -344,8 +346,7 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously // Call out to the delegate. if (_delegateFlags.delegateDidLoadImageWithInfo) { ASDN::MutexUnlocker l(__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = ASNetworkImageSourceSynchronousCache; + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:url sourceType:ASNetworkImageSourceSynchronousCache downloadIdentifier:nil userInfo:nil]; [_delegate imageNode:self didLoadImage:result info:info]; } else if (_delegateFlags.delegateDidLoadImage) { ASDN::MutexUnlocker l(__instanceLock__); @@ -553,7 +554,7 @@ - (void)_locked_cancelImageDownloadWithResumePossibility:(BOOL)storeResume _cacheUUID = nil; } -- (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier))finished +- (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier, id userInfo))finished { ASPerformBlockOnBackgroundThread(^{ NSURL *url; @@ -572,9 +573,9 @@ - (void)_downloadImageWithCompletion:(void (^)(id ima downloadIdentifier = [_downloader downloadImageWithURL:url callbackQueue:dispatch_get_main_queue() downloadProgress:NULL - completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { if (finished != NULL) { - finished(imageContainer, error, downloadIdentifier); + finished(imageContainer, error, downloadIdentifier, userInfo); } }]; as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url); @@ -629,15 +630,15 @@ - (void)_lazilyLoadImageIfNecessary } if (_shouldCacheImage) { - [self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]]; + [self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]]; } else { // First try to load the path directly, for efficiency assuming a developer who // doesn't want caching is trying to be as minimal as possible. - UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:_URL.path]; + UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:URL.path]; if (nonAnimatedImage == nil) { // If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the // extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage. - NSString *filename = [[NSBundle mainBundle] pathForResource:_URL.path.lastPathComponent ofType:nil]; + NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil]; if (filename != nil) { nonAnimatedImage = [UIImage imageWithContentsOfFile:filename]; } @@ -646,7 +647,7 @@ - (void)_lazilyLoadImageIfNecessary // If the file may be an animated gif and then created an animated image. id animatedImage = nil; if (_downloaderFlags.downloaderImplementsAnimatedImage) { - NSData *data = [NSData dataWithContentsOfURL:_URL]; + NSData *data = [NSData dataWithContentsOfURL:URL]; if (data != nil) { animatedImage = [_downloader animatedImageWithData:data]; @@ -669,8 +670,7 @@ - (void)_lazilyLoadImageIfNecessary if (_delegateFlags.delegateDidLoadImageWithInfo) { ASDN::MutexUnlocker u(__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = ASNetworkImageSourceFileURL; + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil]; [delegate imageNode:self didLoadImage:self.image info:info]; } else if (_delegateFlags.delegateDidLoadImage) { ASDN::MutexUnlocker u(__instanceLock__); @@ -679,7 +679,7 @@ - (void)_lazilyLoadImageIfNecessary }); } else { __weak __typeof__(self) weakSelf = self; - auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSource imageSource) { + auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSourceType imageSource, id userInfo) { ASPerformBlockOnBackgroundThread(^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { @@ -718,6 +718,9 @@ - (void)_lazilyLoadImageIfNecessary strongSelf->_downloadIdentifier = nil; strongSelf->_cacheUUID = nil; + // TODO: Why dispatch to main here? + // The docs say the image node delegate methods + // are called from background. ASPerformBlockOnMainThread(^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { @@ -730,8 +733,7 @@ - (void)_lazilyLoadImageIfNecessary if (imageContainer != nil) { if (strongSelf->_delegateFlags.delegateDidLoadImageWithInfo) { ASDN::MutexUnlocker u(strongSelf->__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = imageSource; + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:imageSource downloadIdentifier:downloadIdentifier userInfo:userInfo]; [delegate imageNode:strongSelf didLoadImage:strongSelf.image info:info]; } else if (strongSelf->_delegateFlags.delegateDidLoadImage) { ASDN::MutexUnlocker u(strongSelf->__instanceLock__); @@ -766,20 +768,20 @@ - (void)_lazilyLoadImageIfNecessary } if ([imageContainer asdk_image] == nil && _downloader != nil) { - [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier) { - finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload); + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); }]; } else { as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); - finished(imageContainer, nil, nil, ASNetworkImageSourceAsynchronousCache); + finished(imageContainer, nil, nil, ASNetworkImageSourceAsynchronousCache, nil); } }; [_cache cachedImageWithURL:URL callbackQueue:dispatch_get_main_queue() completion:completion]; } else { - [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier) { - finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload); + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); }]; } } diff --git a/Source/AsyncDisplayKit.h b/Source/AsyncDisplayKit.h index a3935ec21..241d8f2bb 100644 --- a/Source/AsyncDisplayKit.h +++ b/Source/AsyncDisplayKit.h @@ -35,6 +35,7 @@ #import #import #import +#import #import #import diff --git a/Source/Details/ASBasicImageDownloader.h b/Source/Details/ASBasicImageDownloader.h index 7238048c0..d1f8862d4 100644 --- a/Source/Details/ASBasicImageDownloader.h +++ b/Source/Details/ASBasicImageDownloader.h @@ -25,14 +25,15 @@ NS_ASSUME_NONNULL_BEGIN @interface ASBasicImageDownloader : NSObject /** - * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is `nil`. * * This is a very basic image downloader. It does not support caching, progressive downloading and likely * isn't something you should use in production. If you'd like something production ready, see @c ASPINRemoteImageDownloader * * @note It is strongly recommended you include PINRemoteImage and use @c ASPINRemoteImageDownloader instead. */ -+ (instancetype)sharedImageDownloader; +@property (class, readonly) ASBasicImageDownloader *sharedImageDownloader; + (instancetype)new __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); - (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); diff --git a/Source/Details/ASBasicImageDownloader.mm b/Source/Details/ASBasicImageDownloader.mm index 0d8084265..ab88dfa4e 100644 --- a/Source/Details/ASBasicImageDownloader.mm +++ b/Source/Details/ASBasicImageDownloader.mm @@ -130,7 +130,7 @@ - (void)completeWithImage:(UIImage *)image error:(NSError *)error if (completionBlock) { dispatch_async(callbackQueue, ^{ - completionBlock(image, error, nil); + completionBlock(image, error, nil, nil); }); } } @@ -206,7 +206,7 @@ @interface ASBasicImageDownloader () @implementation ASBasicImageDownloader -+ (instancetype)sharedImageDownloader ++ (ASBasicImageDownloader *)sharedImageDownloader { static ASBasicImageDownloader *sharedImageDownloader = nil; static dispatch_once_t once = 0; @@ -235,9 +235,9 @@ - (instancetype)_init #pragma mark ASImageDownloaderProtocol. - (id)downloadImageWithURL:(NSURL *)URL - callbackQueue:(dispatch_queue_t)callbackQueue - downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress - completion:(ASImageDownloaderCompletion)completion + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion { ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL]; diff --git a/Source/Details/ASImageProtocols.h b/Source/Details/ASImageProtocols.h index 3fdf321e4..c99867219 100644 --- a/Source/Details/ASImageProtocols.h +++ b/Source/Details/ASImageProtocols.h @@ -72,8 +72,9 @@ typedef void(^ASImageCacherCompletion)(id _Nullable i @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. @param downloadIdentifier The identifier for the download task that completed. + @param userInfo Any additional info that your downloader would like to communicate through Texture. */ -typedef void(^ASImageDownloaderCompletion)(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier); +typedef void(^ASImageDownloaderCompletion)(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo); /** @param progress The progress of the download, in the range of (0.0, 1.0), inclusive. diff --git a/Source/Details/ASPINRemoteImageDownloader.h b/Source/Details/ASPINRemoteImageDownloader.h index 179104901..abccd9f84 100644 --- a/Source/Details/ASPINRemoteImageDownloader.h +++ b/Source/Details/ASPINRemoteImageDownloader.h @@ -28,12 +28,13 @@ NS_ASSUME_NONNULL_BEGIN @interface ASPINRemoteImageDownloader : NSObject /** - * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is an instance of `PINRemoteImageManagerResult`. * * This is the default downloader used by network backed image nodes if PINRemoteImage and PINCache are * available. It uses PINRemoteImage's features to provide caching and progressive image downloads. */ -+ (ASPINRemoteImageDownloader *)sharedDownloader; +@property (class, readonly) ASPINRemoteImageDownloader *sharedDownloader; /** diff --git a/Source/Details/ASPINRemoteImageDownloader.m b/Source/Details/ASPINRemoteImageDownloader.m index 3b46fc314..3d050fd9e 100644 --- a/Source/Details/ASPINRemoteImageDownloader.m +++ b/Source/Details/ASPINRemoteImageDownloader.m @@ -114,7 +114,7 @@ @interface ASPINRemoteImageDownloader () @implementation ASPINRemoteImageDownloader -+ (instancetype)sharedDownloader ++ (ASPINRemoteImageDownloader *)sharedDownloader { static dispatch_once_t onceToken = 0; @@ -235,12 +235,12 @@ - (nullable id)downloadImageWithURL:(NSURL *)URL [ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{ #if PIN_ANIMATED_AVAILABLE if (result.alternativeRepresentation) { - completion(result.alternativeRepresentation, result.error, result.UUID); + completion(result.alternativeRepresentation, result.error, result.UUID, result); } else { - completion(result.image, result.error, result.UUID); + completion(result.image, result.error, result.UUID, result); } #else - completion(result.image, result.error, result.UUID); + completion(result.image, result.error, result.UUID, result); #endif }]; }; diff --git a/Source/Private/ASNetworkImageLoadInfo+Private.h b/Source/Private/ASNetworkImageLoadInfo+Private.h new file mode 100644 index 000000000..db0435231 --- /dev/null +++ b/Source/Private/ASNetworkImageLoadInfo+Private.h @@ -0,0 +1,22 @@ +// +// ASNetworkImageLoadInfo+Private.h +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASNetworkImageLoadInfo () + +- (instancetype)initWithURL:(NSURL *)url + sourceType:(ASNetworkImageSourceType)sourceType + downloadIdentifier:(nullable id)downloadIdentifier + userInfo:(nullable id)userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/ASBasicImageDownloaderTests.m b/Tests/ASBasicImageDownloaderTests.m index 2dd7d3375..6144b5c25 100644 --- a/Tests/ASBasicImageDownloaderTests.m +++ b/Tests/ASBasicImageDownloaderTests.m @@ -38,14 +38,14 @@ - (void)testAsynchronouslyDownloadTheSameURLTwice [downloader downloadImageWithURL:URL callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) downloadProgress:nil - completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { [firstExpectation fulfill]; }]; [downloader downloadImageWithURL:URL callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) downloadProgress:nil - completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { [secondExpectation fulfill]; }]; diff --git a/Tests/ASMultiplexImageNodeTests.m b/Tests/ASMultiplexImageNodeTests.m index 9eb41874b..71b1fe05e 100644 --- a/Tests/ASMultiplexImageNodeTests.m +++ b/Tests/ASMultiplexImageNodeTests.m @@ -238,7 +238,7 @@ - (void)testUncachedDownload // Simulate completion. ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:5]; - completionBlock([self _testImage], nil, nil); + completionBlock([self _testImage], nil, nil, nil); }); NSNumber *imageIdentifier = @1; diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index d424587c6..a094ad560 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -44,6 +44,9 @@ #define InsetForHeader UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER) #define InsetForFooter UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER) +@interface PhotoCellNode () +@end + @implementation PhotoCellNode { PhotoModel *_photoModel; @@ -77,6 +80,7 @@ - (instancetype)initWithPhotoObject:(PhotoModel *)photo; }]; _photoImageNode = [[ASNetworkImageNode alloc] init]; + _photoImageNode.delegate = self; _photoImageNode.URL = photo.URL; _photoImageNode.layerBacked = YES; @@ -284,6 +288,19 @@ - (void)didEnterPreloadState }]; } +#pragma mark - Network Image Delegate + +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageLoadInfo *)info +{ + // Docs say method is called from bg but right now it's called from main. + // Save main thread time by shunting this. + if (info.sourceType == ASNetworkImageSourceDownload) { + ASPerformBlockOnBackgroundThread(^{ + NSLog(@"Received image %@ from %@ with userInfo %@", image, info.url.path, ASObjectDescriptionMakeTiny(info.userInfo)); + }); + } +} + #pragma mark - Helper Methods - (ASTextNode *)createLayerBackedTextNodeWithString:(NSAttributedString *)attributedString