From 0f4c90699e8fc631172501362bf8e8bc0f45d566 Mon Sep 17 00:00:00 2001 From: Arron Kukadia Date: Fri, 17 Jul 2026 15:43:05 +0100 Subject: [PATCH] fix(ios): load image props without the legacy bridge on bridgeless --- package/ios/RNCSliderComponentView.mm | 63 ++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/package/ios/RNCSliderComponentView.mm b/package/ios/RNCSliderComponentView.mm index 605487a8..74a9537b 100644 --- a/package/ios/RNCSliderComponentView.mm +++ b/package/ios/RNCSliderComponentView.mm @@ -9,6 +9,7 @@ #import #import "RCTImagePrimitivesConversions.h" #import +#import #import "RCTFabricComponentsPlugins.h" #import "RNCSlider.h" @@ -273,23 +274,61 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & } -// TODO temporarily using bridge, workaround for https://github.com/reactwg/react-native-new-architecture/discussions/31#discussioncomment-2717047, rewrite when Meta comes with a solution. +// Legacy-bridge path first: when the bridge is present, RCTImageLoader provides caching, +// decode-time sizing and custom RCTImageURLLoader schemes. On bridgeless, [RCTBridge currentBridge] +// may be nil or expose an ImageLoader that cannot load +// (https://github.com/reactwg/react-native-new-architecture/discussions/31#discussioncomment-2717047), +// so fall back to fetching the bytes directly with NSURLSession. The fallback covers file:// +// (bundled require() assets) and http(s) (Metro in dev, remote images) and honours custom request +// headers; it does not support RCTImageURLLoader plugin schemes (e.g. ph://) or ImageLoader's +// resizing and caching. TODO: replace both paths with the Fabric ImageManager pipeline. - (void)loadImageFromImageSource:(ImageSource)source completionBlock:(RNCLoadImageCompletionBlock)completionBlock failureBlock:(RNCLoadImageFailureBlock)failureBlock { NSString *uri = [[NSString alloc] initWithUTF8String:source.uri.c_str()]; - if ((BOOL)uri.length) { - [[[RCTBridge currentBridge] moduleForName:@"ImageLoader"] - loadImageWithURLRequest:NSURLRequestFromImageSource(source) - size:CGSizeMake(source.size.width, source.size.height) - scale:source.scale - clipped:NO - resizeMode:RCTResizeModeCover - progressBlock:nil - partialLoadBlock:nil - completionBlock:completionBlock]; - } else { + if (!(BOOL)uri.length) { failureBlock(); + return; + } + + NSURLRequest *request = NSURLRequestFromImageSource(source); + CGFloat scale = source.scale > 0 ? source.scale : RCTScreenScale(); + + void (^loadDirectly)(void) = ^{ + [[[NSURLSession sharedSession] dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + UIImage *image = data ? [UIImage imageWithData:data scale:scale] : nil; + // The session queue is a background queue; callers touch UIKit, so hop to main. + dispatch_async(dispatch_get_main_queue(), ^{ + if (image) { + completionBlock(nil, image); + } else { + failureBlock(); + } + }); + }] resume]; + }; + + id imageLoader = [[RCTBridge currentBridge] moduleForName:@"ImageLoader"]; + if (!imageLoader) { + loadDirectly(); + return; } + + [imageLoader + loadImageWithURLRequest:request + size:CGSizeMake(source.size.width, source.size.height) + scale:source.scale + clipped:NO + resizeMode:RCTResizeModeCover + progressBlock:nil + partialLoadBlock:nil + completionBlock:^(NSError *error, UIImage *image) { + if (image) { + completionBlock(nil, image); + } else { + loadDirectly(); + } + }]; } - (void)setInverted:(BOOL)inverted