Closed norbusonam closed 5 months ago
In my scenario, I used
expo-media-library
to retrieve the uri's. I built my app with the old architecture, and this error did not persist.
As this is related to expo-media-library
(which lives here https://github.com/expo/expo/tree/main/packages/expo-media-library) and their support for New Architecture, you should open this issue against Expo's repo.
In my scenario, I used
expo-media-library
to retrieve the uri's. I built my app with the old architecture, and this error did not persist.As this is related to
expo-media-library
(which lives here https://github.com/expo/expo/tree/main/packages/expo-media-library) and their support for New Architecture, you should open this issue against Expo's repo.
You donβt have to use expo-media-library
. This issue persists with any ph://*
uri
Could you please reopen this @cortinico? I boiled it down to something simpler to show that it is a react native issue, and not an expo-media-library
issue:
running the following code fails on the new architecture:
import React from 'react';
import { Image } from 'react-native';
const App = () => {
return <Image source={{ uri: 'ph://B84E8479-475C-4727-A4A4-B77AA9980897' }} style={{ width: 100, height: 100 }} />;
};
export default App;
but it runs exactly as expected in the old one. It looks like the new architecture can not handle iOS's ph://
routes.
I will update the original issue to make it clearer.
Thanks!
please save me!!!!!
for any one who needs a temporary workaround, I found that expo's image component works with ph://
routes.
cc @mingyeom1
any update or ideas around what might cause this bug? I've been using expo-image
in the meantime, but now I've run into the issue that expo-image
does not render ph://
images when the user grants limited permissions (in both new and old architecture) π.
What does the ph://
prefix mean? What kind of permissions does the user need to grant?
What does the
ph://
prefix mean? What kind of permissions does the user need to grant?
Just mean that the URI starts with ph://
. These URI's route to PHAssets in iOS (basically just photo library assets).
That issue with permissions was just an expo-image
thing.
The problem here is that React Native's core Image
component errors when it tries to render any image with a ph://
on the new architecture. I confirmed this was a regression from the old architecture.
EDIT: To clarify, the user does need to grant either limited or full access to photo library to render a photo library image, but that is not the main issue here
linking to where the error is being triggered: code
@react-native-camera-roll/camera-roll
appears to be doing some handling of ph://
routes: code
maybe we can override getModuleInstanceFromClass in AppDelegate to add RNCAssetsLibraryRequestHandler
to the list?
Sharing some findings and a solution that worked for me:
According to this issue React Native no longer support's ph://
routes after @react-native-camera-roll/camera-roll
was separated, and installing @react-native-camera-roll/camera-roll
should integrate the proper handler. However, according to this issue the new architecture hard codes RCTURLRequestHandler
, which means simply installing @react-native-camera-roll/camera-roll
won't be enough to solve the problem.
Ultimetly, I was able to work around this by:
@react-native-camera-roll/camera-roll
getModuleInstanceFromClass
in your AppDelegate.mm
to use @react-native-camera-roll/camera-roll
's RNCAssetsLibraryRequestHandler
handler:
...
...
...
## Extra Info
After these finding's I realized the reason `ph://` routes were working on the old architecture was likely because expo provides a `EXImageLoader`. And since the new architecture hardcodes `RCTURLRequestHandler`, it did now work there.
Hi @norbusonam, thank you for opening the issue and to explore it further. We are aware of this limitation for the new Architecture, unfortunately. I see you found the PR trying to fix that, but we are not convinced by that fix yet.
Your override is a good workaround for the time being, while we find a better solution.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
up, we don't want for this issue to be reaped. We will work on this next half.
I'm looking into this. I was not around when the old architecture was the only architecture...
What's the expected behavior? Was the old architecture able to open urls in the form of ph://<UUID>
? Or was it just dying silently and now it is raising the redbox?
As far as I can see, we always relied on 3rd party libraries to load ph://
. As @norbusonam mentioned, it's possible to use react-native-camera-roll
. Thanks to https://github.com/facebook/react-native/pull/43046, libraries can now (from 0.74) self-register as handlers for ph://
.
I'm trying to understand what's the expectation for a react native project with no libraries.
Perhaps @tsapeta, @WoLewicki or @mrousavy knows something about this? How was React Native behaving in the Old Architecture? Has it ever worked with ph://
urls out of the box?
I remember <Image>
works with ph://
in old arch, I use it in ShadowLens. It should be supported by UIImage loading out of the box afaik.
I think it did work previously. We were using it in our examples for expo-media-library
before we switched to expo-image
.
After a quick look, I'm not actually sure why π
Rendering them would require using the Photos
framework to load them, specifically its PHImageManager
and PHAsset
classes. It needs special handling since there are some more configuration options, e.g. whether to allow downloading the asset from iCloud and the requested quality (lower quality previews might be stored on device without the need to download them). Here is how we're doing that in expo-image
: https://github.com/expo/expo/blob/main/packages/expo-image/ios/Loaders/PhotoLibraryAssetLoader.swift
The required permission is another thing.
Maybe the URL loader somehow supported it and treated as a network request?
After these finding's I realized the reason
ph://
routes were working on the old architecture was likely because expo provides aEXImageLoader
.
It's definitely not the case. This loader doesn't do anything beyond what RN's loaders support and we can actually remove it as it's no longer used.
@norbusonam @mrousavy @tsapeta I spin up an app with 0.71.1, and the Old Architecture fails in the same way as the new one.
Repro:
npx react-native@0.71.1 init TestImage71_1 --version 0.71.1 #this installs pods for the old arch by default.
cd TestImage71_1/ios
open TestImage71_1.xcworkspace
cd ..
yarn start
I retrieved the ID by:
NSPhotoLibraryUsageDescription
in the capabilities in XcodePhotos
framework in the Frameworks, Libraries and Embedded Contents in the General tab of the project in Xcode#import <Photos/PHAsset.h>
//...
PHFetchResult<PHAsset *> * res = [PHAsset fetchAssetsWithOptions:nil];
PHAsset * first = res.firstObject;
NSLog(@"Id: %@", first.localIdentifier);
Then, replace the App.tsx content with the following:
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
useColorScheme,
Image,
View,
} from 'react-native';
import {Colors} from 'react-native/Libraries/NewAppScreen';
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Image
source={{uri: 'ph://106E99A1-4F6A-45A2-B320-B0AD4A8E8473'}}
style={{width: 100, height: 100}}
/>
</View>
</ScrollView>
</SafeAreaView>
);
}
export default App;
For my simulator, 106E99A1-4F6A-45A2-B320-B0AD4A8E8473
is a valid id for an asset in the camera roll.
When I run the app in the Old Architecture, I get the same error message:
Could it be that, in the Old Architecture, the libraries were able to self register their image handler in the list of handlers? So, what I think was happening:
yarn add react-native-cameraroll
#and example of library that can handle ph://
url schemasNSPhotoLibraryUsageDescription
And it looked like React Native is able to manage the ph://
directly, but it was actually a library handling that?
That fits with my understanding and as a difference between New/Old Arch. In fact, libraries couldn't, before 0.74, register themselves as URL Handlers/Image Loaders in the New Architecture.
Closing this as it is an intended React Native behaviour.
The Old Architecture is relying on some library to pick up the ph://
protocol. Libraries on the Old Architecture can register automatically to respond to custom URL protocols, that's why it looked like React Native was able to load images using the ph://
protocol.
In the New Architecture, before 0.74, libraries could not register themselves as URL handler, hence, this issue has been opened. In 0.74, thanks to this PR, libraries can now register themselves as URL handler, filling the gap between the Old and the New architecture.
Hey @cipolleschi - this makes perfect sense, thanks for the explanation.
I'm not sure if I understand how to properly register custom image loaders (or URL handlers) now on the new architecture though? Is there a method I can use to register a custom handler in my AppDelegate?
RN CameraRoll still has that custom PH asset handler, but it doesn't work anymore. see https://github.com/react-native-cameraroll/react-native-cameraroll/issues/631
@mrousavy I found this conversation: https://github.com/facebook/react-native/pull/36071#issuecomment-1914950992. Seems like for some reason the links to commits are broken and lead to some weird, non-connected ones. I can quickly try to find the exact commit that introduces the changes @cipolleschi mentioned.
Looks like it is this one: https://github.com/facebook/react-native/pull/42923
Got it!
For everyone else stumbling upon this issue, here's what I did:
Create a file that implements the RCTImageURLLoader
protocol, I'll call it RNCAssetsLibraryRequestHandler.h
:
#pragma once
#import <Foundation/Foundation.h>
#import <React/RCTImageURLLoader.h>
@class PHPhotoLibrary;
@interface RNCAssetsLibraryRequestHandler : NSObject <RCTImageURLLoader>
@end
Implement the two methods, and also treat it as an RCTBridgeModule (it has (NSString*)moduleName
):
#import "RNCAssetsLibraryRequestHandler.h"
@implementation RNCAssetsLibraryRequestHandler
- (BOOL)canLoadImageURL:(NSURL *)requestURL {
return [requestURL.scheme isEqualToString:@"ph"];
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler {
// TODO: Load ph:// asset here
}
+ (NSString *)moduleName {
return @"RNCAssetsLibraryRequestHandler";
}
@end
RNCAssetsLibraryRequestHandler
to your lib's (or app's) codegen, there's a modulesConformingToProtocol
option:
"codegenConfig": {
...
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": "RNCAssetsLibraryRequestHandler"
}
},
Install pods & rebuild, and now the ph://
handler is registered π
@WoLewicki I'll create a PR to CameraRoll :)
@cortinico two things I wanna note here;
RNCAssetsLibraryRequestHandler
as a RCTBridgeModule
(at least that's the protocol that it's implementing through CodeGen) - is that still new arch stuff? For me it runs fine in bridgeless, so I was confused about this.codegenConfig
? I feel like this would've been easier to solve if there was a section in the docs about what you can actually do with codegenConfig
, ideally something that automatically generates an API documentation based off of Flow/TS types of the codegen plugin.Ah - RCTImageURLLoader
extends RCTBridgeModule
. Is that correct? Does that need to be an RCTBridgeModule?
It won't have any methods or so anyways, so I'm thinking why not just have this be a simple ObjC class?
I just created a PR in react-native-cameraroll to add custom Image URL loaders for ph://
and asset-library://
assets. https://github.com/react-native-cameraroll/react-native-cameraroll/pull/632
So if you install react-native-cameraroll, ph://
assets can be loaded again in <Image>
components on new arch/Fabric ππ₯³
What is the fix for expo-media-library
, though, since this issue seems to be relevant on version ~16.0.3
and with expo 51.0.11?
Just as a context, I am using the default Image
component of React Native and I am on old architecture
What is the fix for
expo-media-library
, though, since this issue seems to be relevant on version~16.0.3
and with expo 51.0.11?Just as a context, I am using the default
Image
component of React Native and I am on old architecture
+1
What is the fix for
expo-media-library
, though, since this issue seems to be relevant on version~16.0.3
and with expo 51.0.11?Just as a context, I am using the default
Image
component of React Native and I am on old architecture
Expo's image library is compatible with ph://
assets
You can also get the local URI of an asset by doing (MediaLibrary.getAssetInfoAsync(asset)).localUri
It turned out we had a regression causing the expo-media-library
to not include the image loader for ph://
urls so the standard RN Image couldn't handle them. It's been fixed in version 16.0.4
.
I am using 16.0.4
for expo-media-library
but still ph://
is not accepted by <Image>
But I found workaround as well. As https://github.com/facebook/react-native/issues/36136#issuecomment-2181326761 says, (MediaLibrary.getAssetInfoAsync(asset)).localUri
saved my life.
I left the workaround / error in my repo as well. Hopefully it helps someone
Description
When giving the
Image
component auri
beginning withph://
like so:the image fails to render:
In my scenario, I used
expo-media-library
to retrieve the uri's. I built my app with the old architecture, and this error did not persist.Version
0.71.1
Output of
npx react-native info
System: OS: macOS 13.2 CPU: (8) arm64 Apple M1 Memory: 75.11 MB / 16.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.13.0 - ~/.nvm/versions/node/v18.13.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v18.13.0/bin/yarn npm: 8.19.3 - ~/.nvm/versions/node/v18.13.0/bin/npm Watchman: 2023.01.30.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1 Android SDK: Not Found IDEs: Android Studio: Not Found Xcode: 14.2/14C18 - /usr/bin/xcodebuild Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 18.2.0 => 18.2.0 react-native: 0.71.1 => 0.71.1 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found
Steps to reproduce
ph://
by any meansSnack, code example, screenshot, or link to a repository
Not sure how to switch a snack into the new architecture, but this code in the new architecture will fail:
https://snack.expo.dev/gv6bMd2MY?platform=ios
UPDATE: Providing a simpler example to convey that this is a react native issue and not due to any dependency. The following code fails on the new archatecure with the error above:
but it runs exactly as expected in the old one. It looks like the new architecture can not handle iOS's ph:// routes.