diff --git a/Libraries/Image/RCTImageLoader.mm b/Libraries/Image/RCTImageLoader.mm index 1c77a21986e..721badeaa92 100644 --- a/Libraries/Image/RCTImageLoader.mm +++ b/Libraries/Image/RCTImageLoader.mm @@ -507,7 +507,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, completionHandler:^(NSError *error, UIImage *image) { completionHandler(error, image, nil); }]; - return [[RCTImageURLLoaderRequest alloc] initWithRequestId:nil cancellationBlock:cb]; + return [[RCTImageURLLoaderRequest alloc] initWithRequestId:nil imageURL:request.URL cancellationBlock:cb]; } // All access to URL cache must be serialized @@ -574,7 +574,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, } }); - return [[RCTImageURLLoaderRequest alloc] initWithRequestId:requestId cancellationBlock:^{ + return [[RCTImageURLLoaderRequest alloc] initWithRequestId:requestId imageURL:request.URL cancellationBlock:^{ BOOL alreadyCancelled = atomic_fetch_or(cancelled.get(), 1); if (alreadyCancelled) { return; @@ -700,6 +700,8 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, }; } +#pragma mark - RCTImageLoaderWithAttributionProtocol + - (RCTImageURLLoaderRequest *)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest size:(CGSize)size scale:(CGFloat)scale @@ -777,7 +779,42 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, partialLoadBlock:partialLoadBlock completionBlock:completionHandler]; cancelLoad = loaderRequest.cancellationBlock; - return [[RCTImageURLLoaderRequest alloc] initWithRequestId:loaderRequest.requestId cancellationBlock:cancellationBlock]; + return [[RCTImageURLLoaderRequest alloc] initWithRequestId:loaderRequest.requestId imageURL:imageURLRequest.URL cancellationBlock:cancellationBlock]; +} + +- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest +{ + if (!loaderRequest) { + return; + } + + id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; + if ([loadHandler respondsToSelector:@selector(trackURLImageContentDidSetForRequest:)]) { + [(id)loadHandler trackURLImageContentDidSetForRequest:loaderRequest]; + } +} + +- (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(UIView *)imageView +{ + if (!loaderRequest || !imageView) { + return; + } + + id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; + if ([loadHandler respondsToSelector:@selector(trackURLImageVisibilityForRequest:imageView:)]) { + [(id)loadHandler trackURLImageVisibilityForRequest:loaderRequest imageView:imageView]; + } +} + +- (void)trackURLImageDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest +{ + if (!loaderRequest) { + return; + } + id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; + if ([loadHandler respondsToSelector:@selector(trackURLImageDidDestroy:)]) { + [(id)loadHandler trackURLImageDidDestroy:loaderRequest]; + } } - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data diff --git a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h index c785369c0bf..119fb8fb5ae 100644 --- a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h +++ b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h @@ -30,5 +30,19 @@ RCT_EXTERN void RCTEnableImageLoadingPerfInstrumentation(BOOL enabled); progressBlock:(RCTImageLoaderProgressBlock)progressBlock partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; +/** + * Image instrumentation - notify that the image content (UIImage) has been set on the native view. + */ +- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest; + +/** + * Image instrumentation - start tracking the on-screen visibility of the native image view. + */ +- (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(UIView *)imageView; + +/** + * Image instrumentation - notify that the native image view was destroyed. + */ +- (void)trackURLImageDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest; @end diff --git a/Libraries/Image/RCTImageURLLoaderWithAttribution.h b/Libraries/Image/RCTImageURLLoaderWithAttribution.h index 66aeda26dc0..3423f545fde 100644 --- a/Libraries/Image/RCTImageURLLoaderWithAttribution.h +++ b/Libraries/Image/RCTImageURLLoaderWithAttribution.h @@ -21,9 +21,10 @@ struct ImageURLLoaderAttribution { @interface RCTImageURLLoaderRequest : NSObject @property (nonatomic, strong, readonly) NSString *requestId; +@property (nonatomic, strong, readonly) NSURL *imageURL; @property (nonatomic, copy, readonly) RCTImageLoaderCancellationBlock cancellationBlock; -- (instancetype)initWithRequestId:(NSString *)requestId cancellationBlock:(RCTImageLoaderCancellationBlock)cancellationBlock; +- (instancetype)initWithRequestId:(NSString *)requestId imageURL:(NSURL *)imageURL cancellationBlock:(RCTImageLoaderCancellationBlock)cancellationBlock; - (void)cancel; @end @@ -48,4 +49,19 @@ struct ImageURLLoaderAttribution { partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; +/** + * Image instrumentation - notify that the image content (UIImage) has been set on the native view. + */ +- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest; + +/** + * Image instrumentation - start tracking the on-screen visibility of the native image view. + */ +- (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(UIView *)imageView; + +/** + * Image instrumentation - notify that the native image view was destroyed. + */ +- (void)trackURLImageDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest; + @end diff --git a/Libraries/Image/RCTImageURLLoaderWithAttribution.mm b/Libraries/Image/RCTImageURLLoaderWithAttribution.mm index 36e514b2288..61baa4aafee 100644 --- a/Libraries/Image/RCTImageURLLoaderWithAttribution.mm +++ b/Libraries/Image/RCTImageURLLoaderWithAttribution.mm @@ -9,10 +9,11 @@ @implementation RCTImageURLLoaderRequest -- (instancetype)initWithRequestId:(NSString *)requestId cancellationBlock:(RCTImageLoaderCancellationBlock)cancellationBlock +- (instancetype)initWithRequestId:(NSString *)requestId imageURL:(NSURL *)imageURL cancellationBlock:(RCTImageLoaderCancellationBlock)cancellationBlock { if (self = [super init]) { _requestId = requestId; + _imageURL = imageURL; _cancellationBlock = cancellationBlock; } diff --git a/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp b/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp index 8f41d93231c..35475d10831 100644 --- a/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp +++ b/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp @@ -38,5 +38,17 @@ const std::shared_ptr // Not implemented abort(); } + +const std::shared_ptr + &ImageRequest::getSharedImageInstrumentation() const { + // Not implemented + abort(); +} + +const ImageInstrumentation &ImageRequest::getImageInstrumentation() const { + // Not implemented + abort(); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h index 3a2f2b477f7..5d8f84f0f76 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h @@ -20,15 +20,18 @@ namespace react { class RCTImageInstrumentationProxy final : public ImageInstrumentation { public: RCTImageInstrumentationProxy(id imageLoader); + ~RCTImageInstrumentationProxy(); void didSetImage() const override; void didEnterVisibilityRange() const override; void didExitVisibilityRange() const override; void trackNativeImageView(UIView *imageView) const; + void setImageURLLoaderRequest(RCTImageURLLoaderRequest *request); private: __weak id imageLoader_; + RCTImageURLLoaderRequest *imageURLLoaderRequest_; }; } // namespace react diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm index 0e4b67ee615..1ed24f9faac 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm @@ -15,15 +15,25 @@ RCTImageInstrumentationProxy::RCTImageInstrumentationProxy(id(_imageLoader)); + auto imageInstrumentation = std::make_shared(_imageLoader); + auto imageRequest = ImageRequest(imageSource, imageInstrumentation); auto weakObserverCoordinator = (std::weak_ptr)imageRequest.getSharedObserverCoordinator(); @@ -97,6 +98,10 @@ using namespace facebook::react; completionBlock:completionBlock]; RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock; sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); }); + + if (imageInstrumentation) { + imageInstrumentation->setImageURLLoaderRequest(loaderRequest); + } }); return imageRequest;