Swiftgram/AsyncDisplayKit/Details/ASBasicImageDownloader.mm
Ryan Nystrom c645935631 ASBasicImageDownloader iOS 7 support
On iOS 7, NSURLSessionTask is actually a __NSCFURLSessionTask (private class) and our NSURLSessionTask category to add the associated object asyncdisplaykit_metadata was crashing on iOS 7 stating that there was no method. This commit changes the category to extend NSURLRequest as well as change NSURLSessionTask to NSURLSessionDownloadTask.
2014-12-04 11:25:36 -08:00

157 lines
4.9 KiB
Plaintext

/* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASBasicImageDownloader.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
#import "ASThread.h"
#pragma mark -
/**
* Collection of properties associated with a download request.
*/
@interface ASBasicImageDownloaderMetadata : NSObject
@property (nonatomic, strong) dispatch_queue_t callbackQueue;
@property (nonatomic, strong) void (^downloadProgressBlock)(CGFloat);
@property (nonatomic, strong) void (^completionBlock)(CGImageRef, NSError *);
@end
@implementation ASBasicImageDownloaderMetadata
@end
#pragma mark -
/**
* NSURLSessionDownloadTask lacks a `userInfo` property, so add this association ourselves.
*/
@interface NSURLRequest (ASBasicImageDownloader)
@property (nonatomic, strong) ASBasicImageDownloaderMetadata *asyncdisplaykit_metadata;
@end
@implementation NSURLRequest (ASBasicImageDownloader)
static const char *kMetadataKey = NSStringFromClass(ASBasicImageDownloaderMetadata.class).UTF8String;
- (void)setAsyncdisplaykit_metadata:(ASBasicImageDownloaderMetadata *)asyncdisplaykit_metadata
{
objc_setAssociatedObject(self, kMetadataKey, asyncdisplaykit_metadata, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (ASBasicImageDownloader *)asyncdisplaykit_metadata
{
return objc_getAssociatedObject(self, kMetadataKey);
}
@end
#pragma mark -
@interface ASBasicImageDownloader () <NSURLSessionDownloadDelegate>
{
NSOperationQueue *_sessionDelegateQueue;
NSURLSession *_session;
}
@end
@implementation ASBasicImageDownloader
#pragma mark Lifecycle.
- (instancetype)init
{
if (!(self = [super init]))
return nil;
_sessionDelegateQueue = [[NSOperationQueue alloc] init];
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:_sessionDelegateQueue];
return self;
}
#pragma mark ASImageDownloaderProtocol.
- (id)downloadImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgressBlock:(void (^)(CGFloat))downloadProgressBlock
completion:(void (^)(CGImageRef, NSError *))completion
{
// create download task
NSURLSessionDownloadTask *task = [_session downloadTaskWithURL:URL];
// associate metadata with it
ASBasicImageDownloaderMetadata *metadata = [[ASBasicImageDownloaderMetadata alloc] init];
metadata.callbackQueue = callbackQueue ?: dispatch_get_main_queue();
metadata.downloadProgressBlock = downloadProgressBlock;
metadata.completionBlock = completion;
task.originalRequest.asyncdisplaykit_metadata = metadata;
// start downloading
[task resume];
// return the task as an opaque cancellation token
return task;
}
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
{
if (!downloadIdentifier) {
return;
}
ASDisplayNodeAssert([downloadIdentifier isKindOfClass:NSURLSessionDownloadTask.class], @"unexpected downloadIdentifier");
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)downloadIdentifier;
[task cancel];
}
#pragma mark NSURLSessionDownloadDelegate.
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
ASBasicImageDownloaderMetadata *metadata = downloadTask.originalRequest.asyncdisplaykit_metadata;
if (metadata.downloadProgressBlock) {
metadata.downloadProgressBlock((CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite);
}
}
// invoked if the download succeeded with no error
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
ASBasicImageDownloaderMetadata *metadata = downloadTask.originalRequest.asyncdisplaykit_metadata;
if (metadata.completionBlock) {
dispatch_async(metadata.callbackQueue, ^{
metadata.completionBlock(image.CGImage, nil);
});
}
}
// invoked unconditionally
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)task
didCompleteWithError:(NSError *)error
{
ASBasicImageDownloaderMetadata *metadata = task.originalRequest.asyncdisplaykit_metadata;
if (metadata && error) {
dispatch_async(metadata.callbackQueue, ^{
metadata.completionBlock(NULL, error);
});
}
}
@end