Skip to content

Commit

Permalink
[Image] add onLoadStart, onLoadProgress, onLoadError, onLoadEnd
Browse files Browse the repository at this point in the history
  • Loading branch information
ptmt committed Jul 8, 2015
1 parent 57513fd commit 7c856f5
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 25 deletions.
30 changes: 29 additions & 1 deletion Libraries/Image/Image.ios.js 100644 → 100755
Expand Up @@ -104,7 +104,33 @@ var Image = React.createClass({
*
* {nativeEvent: { layout: {x, y, width, height}}}.
*/
onLayout: PropTypes.func,
onLayout: PropTypes.func,
/**
* Invoked on load start
*/
onLoadStart: PropTypes.func,
/**
* Invoked on download progress with
*
* {nativeEvent: { written, total}}.
*/
onLoadProgress: PropTypes.func,
/**
* Invoked on load abort
*/
onLoadAbort: PropTypes.func,
/**
* Invoked on load error
*
* {nativeEvent: { error}}.
*/
onLoadError: PropTypes.func,
/**
* Invoked on load end
*
*/
onLoaded: PropTypes.func

},

statics: {
Expand Down Expand Up @@ -161,6 +187,7 @@ var Image = React.createClass({
if (this.props.defaultSource) {
nativeProps.defaultImageSrc = this.props.defaultSource.uri;
}
nativeProps.progressHandlerRegistered = isNetwork && this.props.onLoadProgress;
return <RawImage {...nativeProps} />;
}
});
Expand All @@ -178,6 +205,7 @@ var nativeOnlyProps = {
src: true,
defaultImageSrc: true,
imageTag: true,
progressHandlerRegistered: true
};
if (__DEV__) {
verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);
Expand Down
26 changes: 26 additions & 0 deletions Libraries/Image/RCTDownloadTaskWrapper.h
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-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 <Foundation/Foundation.h>

typedef void (^RCTDataCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total);

@interface RCTDownloadTaskWrapper : NSObject <NSURLSessionDownloadDelegate>

@property (copy, nonatomic) RCTDataCompletionBlock completionBlock;
@property (copy, nonatomic) RCTDataProgressBlock progressBlock;

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue;

- (NSURLSessionDownloadTask *)downloadData:(NSURL *) url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock;

@property(nonatomic,assign) id<NSURLSessionDownloadDelegate> delegate;

@end
74 changes: 74 additions & 0 deletions Libraries/Image/RCTDownloadTaskWrapper.m
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2015-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 "RCTDownloadTaskWrapper.h"

@implementation RCTDownloadTaskWrapper
{
NSURLSession *_URLSession;
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue
{
if ((self = [super init])) {
_URLSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
}

return self;
}

- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url
progressBlock:(RCTDataProgressBlock)progressBlock
completionBlock:(RCTDataCompletionBlock)completionBlock
{
self.completionBlock = completionBlock;
self.progressBlock = progressBlock;
NSURLSessionDownloadTask *task = [_URLSession downloadTaskWithURL:url completionHandler:nil];
[task resume];
return task;
}

#pragma mark - NSURLSessionTaskDelegate methods

- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
self.completionBlock(downloadTask.response, data, nil);
});

}

- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)didWriteData
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.progressBlock != nil) {
self.progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
}
});
}

- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.completionBlock(NULL, NULL, error);
});
}
}

@end
6 changes: 6 additions & 0 deletions Libraries/Image/RCTImage.xcodeproj/project.pbxproj 100644 → 100755
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */; };
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; };
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
Expand All @@ -32,6 +33,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTaskWrapper.h; sourceTree = "<group>"; };
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTaskWrapper.m; sourceTree = "<group>"; };
1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = "<group>"; };
1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = "<group>"; };
1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -69,6 +72,8 @@
58B511541A9E6B3D00147676 = {
isa = PBXGroup;
children = (
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */,
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */,
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
Expand Down Expand Up @@ -168,6 +173,7 @@
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Image/RCTImageDownloader.h 100644 → 100755
Expand Up @@ -8,6 +8,7 @@
*/

#import <UIKit/UIKit.h>
#import "RCTDownloadTaskWrapper.h"

typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
Expand All @@ -22,6 +23,7 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
* the main thread. Returns a token that can be used to cancel the download.
*/
- (id)downloadDataForURL:(NSURL *)url
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTDataDownloadBlock)block;

/**
Expand All @@ -35,6 +37,7 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block;

/**
Expand Down
27 changes: 16 additions & 11 deletions Libraries/Image/RCTImageDownloader.m 100644 → 100755
Expand Up @@ -11,6 +11,7 @@

#import "RCTLog.h"
#import "RCTUtils.h"
#import "RCTDownloadTaskWrapper.h"

typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error);

Expand Down Expand Up @@ -45,12 +46,14 @@ - (instancetype)init
return self;
}

- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block
- (id)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block
{
NSString *cacheKey = url.absoluteString;

__block BOOL cancelled = NO;
__block NSURLSessionDataTask *task = nil;
__block NSURLSessionDownloadTask *task = nil;
RCTDownloadTaskWrapper *_downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc]
initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegateQueue:nil];

dispatch_block_t cancel = ^{
cancelled = YES;
Expand Down Expand Up @@ -84,40 +87,41 @@ - (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block
}
});
};
NSURLRequest *request = [NSURLRequest requestWithURL:url];
task = [_downloadTaskWrapper downloadData:url progressBlock:progressBlock
completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {

task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!cancelled) {
runBlocks(NO, data, error);
}

if (response) {
RCTImageDownloader *strongSelf = weakSelf;
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task];
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
}
task = nil;
}];

[_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) {
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cancelled) {
return;
}

if (cachedResponse) {
runBlocks(YES, cachedResponse.data, nil);
} else {
[task resume];
}
}];

}
});

return [cancel copy];
}

- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block
- (id)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block
{
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) {
return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSData *data, NSError *error) {
block(data, error);
}];
}
Expand All @@ -127,9 +131,10 @@ - (id)downloadImageForURL:(NSURL *)url
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block
{
return [self downloadDataForURL:url block:^(NSData *data, NSError *error) {
return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) {
if (!data || error) {
block(nil, error);
return;
Expand Down Expand Up @@ -262,4 +267,4 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
return (CGRect){CGPointZero, destSize};
}
}
}
2 changes: 1 addition & 1 deletion Libraries/Image/RCTImageLoader.m 100644 → 100755
Expand Up @@ -121,7 +121,7 @@ + (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error,
RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
return;
}
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) {
[[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
if (error) {
RCTDispatchCallbackOnMainQueue(callback, error, nil);
} else {
Expand Down
4 changes: 3 additions & 1 deletion Libraries/Image/RCTNetworkImageView.h 100644 → 100755
Expand Up @@ -10,10 +10,12 @@
#import <UIKit/UIKit.h>

@class RCTImageDownloader;
@class RCTEventDispatcher;

@interface RCTNetworkImageView : UIView

- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
imageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER;

/**
* An image that will appear while the view is loading the image from the network,
Expand Down

0 comments on commit 7c856f5

Please sign in to comment.