From aa0a50c518907f9e91e2958867d4cc7cda1bd5be Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 18 Feb 2020 16:48:49 -0800 Subject: [PATCH] Introducing RCTBlockingImageManager, an image manager for tests in Fabric Summary: Changelog: [Internal] This allows to block queue until image is fetched. In paper, the photo of dog will not appear unless the network request is "fast enough". {F228670428} Reviewed By: shergin Differential Revision: D19907906 fbshipit-source-id: 98d68abf1255388f9b97fb9da367d8f583a4220d --- .../imagemanager/platform/ios/ImageManager.mm | 8 +- .../platform/ios/RCTImageManager.h | 6 +- .../platform/ios/RCTImageManagerProtocol.h | 16 +++ .../platform/ios/RCTSyncImageManager.h | 28 ++++++ .../platform/ios/RCTSyncImageManager.mm | 97 +++++++++++++++++++ 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 ReactCommon/fabric/imagemanager/platform/ios/RCTImageManagerProtocol.h create mode 100644 ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.h create mode 100644 ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.mm diff --git a/ReactCommon/fabric/imagemanager/platform/ios/ImageManager.mm b/ReactCommon/fabric/imagemanager/platform/ios/ImageManager.mm index b6183ab2e2bf8f..3d37911aaf1a34 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/ImageManager.mm +++ b/ReactCommon/fabric/imagemanager/platform/ios/ImageManager.mm @@ -8,9 +8,11 @@ #include "ImageManager.h" #import +#import #import #import "RCTImageManager.h" +#import "RCTSyncImageManager.h" namespace facebook { namespace react { @@ -20,7 +22,11 @@ id imageLoader = (id)unwrapManagedObject( contextContainer->at>("RCTImageLoader")); - self_ = (__bridge_retained void *)[[RCTImageManager alloc] initWithImageLoader:imageLoader]; + if (RCTRunningInTestEnvironment()) { + self_ = (__bridge_retained void *)[[RCTSyncImageManager alloc] initWithImageLoader:imageLoader]; + } else { + self_ = (__bridge_retained void *)[[RCTImageManager alloc] initWithImageLoader:imageLoader]; + } } ImageManager::~ImageManager() diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.h b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.h index 7a8303d6afaf16..0b5814990ce490 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.h +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.h @@ -7,9 +7,7 @@ #import -#import -#import -#import +#import NS_ASSUME_NONNULL_BEGIN @@ -18,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /** * iOS-specific ImageManager. */ -@interface RCTImageManager : NSObject +@interface RCTImageManager : NSObject - (instancetype)initWithImageLoader:(id)imageLoader; diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManagerProtocol.h b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManagerProtocol.h new file mode 100644 index 00000000000000..d69c2d3f34c1e2 --- /dev/null +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManagerProtocol.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import +#import + +@protocol RCTImageManagerProtocol + +- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource + surfaceId:(facebook::react::SurfaceId)surfaceId; +@end diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.h b/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.h new file mode 100644 index 00000000000000..c48c0b077ef39d --- /dev/null +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol RCTImageLoaderWithAttributionProtocol; + +/** + * iOS-specific ImageManager. + */ +@interface RCTSyncImageManager : NSObject + +- (instancetype)initWithImageLoader:(id)imageLoader; + +- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource + surfaceId:(facebook::react::SurfaceId)surfaceId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.mm b/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.mm new file mode 100644 index 00000000000000..fcd6382600f1bd --- /dev/null +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTSyncImageManager.mm @@ -0,0 +1,97 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTSyncImageManager.h" + +#import +#import + +#import +#import +#import +#import +#import + +#import "RCTImagePrimitivesConversions.h" + +using namespace facebook::react; + +@implementation RCTSyncImageManager { + id _imageLoader; +} + +- (instancetype)initWithImageLoader:(id)imageLoader +{ + if (self = [super init]) { + RCTAssert(RCTRunningInTestEnvironment(), @"This class is only meant to be used in test environment"); + _imageLoader = imageLoader; + } + + return self; +} + +- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId +{ + auto imageRequest = ImageRequest(imageSource, nullptr); + auto weakObserverCoordinator = + (std::weak_ptr)imageRequest.getSharedObserverCoordinator(); + + auto sharedCancelationFunction = SharedFunction<>(); + imageRequest.setCancelationFunction(sharedCancelationFunction); + + dispatch_group_t imageWaitGroup = dispatch_group_create(); + + dispatch_group_enter(imageWaitGroup); + + NSURLRequest *request = NSURLRequestFromImageSource(imageSource); + + auto completionBlock = ^(NSError *error, UIImage *image) { + auto observerCoordinator = weakObserverCoordinator.lock(); + if (!observerCoordinator) { + return; + } + + if (image && !error) { + observerCoordinator->nativeImageResponseComplete(ImageResponse(wrapManagedObject(image))); + } else { + observerCoordinator->nativeImageResponseFailed(); + } + dispatch_group_leave(imageWaitGroup); + }; + + auto progressBlock = ^(int64_t progress, int64_t total) { + auto observerCoordinator = weakObserverCoordinator.lock(); + if (!observerCoordinator) { + return; + } + + observerCoordinator->nativeImageResponseProgress(progress / (float)total); + }; + + RCTImageURLLoaderRequest *loaderRequest = + [self->_imageLoader loadImageWithURLRequest:request + size:CGSizeMake(imageSource.size.width, imageSource.size.height) + scale:imageSource.scale + clipped:YES + resizeMode:RCTResizeModeStretch + attribution:{ + .surfaceId = surfaceId, + } + progressBlock:progressBlock + partialLoadBlock:nil + completionBlock:completionBlock]; + RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock; + sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); }); + + auto result = dispatch_group_wait(imageWaitGroup, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC)); + if (result != 0) { + RCTLogError(@"Getting an image timed out"); + } + return imageRequest; +} + +@end