Skip to content

Commit

Permalink
Add support for providing additional info to network image node deleg…
Browse files Browse the repository at this point in the history
…ate (TextureGroup#775)

* Add support for piping arbitrary user info from ASImageDownloader to the ASNetworkImageNodeDelegate

* s/source/sourceType

* Fix stuff and take Michael's advice
  • Loading branch information
Adlai-Holler authored and bernieperez committed Apr 25, 2018
1 parent 5fcafbc commit fb1adfa
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 55 deletions.
12 changes: 12 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -913,6 +916,9 @@
CCE4F9BB1F0EA67F00062E4E /* debugbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debugbreak.h; sourceTree = "<group>"; };
CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTLayoutFixture.h; sourceTree = "<group>"; };
CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTLayoutFixture.mm; sourceTree = "<group>"; };
CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASNetworkImageLoadInfo.h; sourceTree = "<group>"; };
CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageLoadInfo.m; sourceTree = "<group>"; };
CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASNetworkImageLoadInfo+Private.h"; sourceTree = "<group>"; };
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 = "<group>"; };
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -1376,6 +1384,7 @@
058D0A01195D050800B7D73C /* Private */ = {
isa = PBXGroup;
children = (
CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */,
CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */,
CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */,
CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Source/ASMultiplexImageNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
completion:^(id <ASImageContainerProtocol> 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)
Expand Down
39 changes: 39 additions & 0 deletions Source/ASNetworkImageLoadInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// ASNetworkImageLoadInfo.h
// AsyncDisplayKit
//
// Created by Adlai on 1/30/18.
// Copyright © 2018 Facebook. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger, ASNetworkImageSourceType) {
ASNetworkImageSourceUnspecified = 0,
ASNetworkImageSourceSynchronousCache,
ASNetworkImageSourceAsynchronousCache,
ASNetworkImageSourceFileURL,
ASNetworkImageSourceDownload,
};

AS_SUBCLASSING_RESTRICTED
@interface ASNetworkImageLoadInfo : NSObject <NSCopying>

/// 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
32 changes: 32 additions & 0 deletions Source/ASNetworkImageLoadInfo.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// ASNetworkImageLoadInfo.m
// AsyncDisplayKit
//
// Created by Adlai on 1/30/18.
// Copyright © 2018 Facebook. All rights reserved.
//

#import <AsyncDisplayKit/ASNetworkImageLoadInfo.h>
#import <AsyncDisplayKit/ASNetworkImageLoadInfo+Private.h>

@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
19 changes: 3 additions & 16 deletions Source/ASNetworkImageNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
NS_ASSUME_NONNULL_BEGIN

@protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol;
@class ASNetworkImageLoadInfo;


/**
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
44 changes: 23 additions & 21 deletions Source/ASNetworkImageNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
#import <AsyncDisplayKit/ASImageContainerProtocolCategories.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASNetworkImageLoadInfo+Private.h>

#if AS_PIN_REMOTE_IMAGE
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
Expand Down Expand Up @@ -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];
Expand All @@ -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__);
Expand Down Expand Up @@ -553,7 +554,7 @@ - (void)_locked_cancelImageDownloadWithResumePossibility:(BOOL)storeResume
_cacheUUID = nil;
}

- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier, id userInfo))finished
{
ASPerformBlockOnBackgroundThread(^{
NSURL *url;
Expand All @@ -572,9 +573,9 @@ - (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> ima
downloadIdentifier = [_downloader downloadImageWithURL:url
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
completion:^(id <ASImageContainerProtocol> _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);
Expand Down Expand Up @@ -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];
}
Expand All @@ -646,7 +647,7 @@ - (void)_lazilyLoadImageIfNecessary
// If the file may be an animated gif and then created an animated image.
id<ASAnimatedImageProtocol> animatedImage = nil;
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
NSData *data = [NSData dataWithContentsOfURL:_URL];
NSData *data = [NSData dataWithContentsOfURL:URL];
if (data != nil) {
animatedImage = [_downloader animatedImageWithData:data];

Expand All @@ -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__);
Expand All @@ -679,7 +679,7 @@ - (void)_lazilyLoadImageIfNecessary
});
} else {
__weak __typeof__(self) weakSelf = self;
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSource imageSource) {
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSourceType imageSource, id userInfo) {
ASPerformBlockOnBackgroundThread(^{
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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__);
Expand Down Expand Up @@ -766,20 +768,20 @@ - (void)_lazilyLoadImageIfNecessary
}

if ([imageContainer asdk_image] == nil && _downloader != nil) {
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> 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<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo);
}];
}
}
Expand Down
Loading

0 comments on commit fb1adfa

Please sign in to comment.