React Native - Save Network Image to iOS Camera Roll - Solved: error PHPhotosErrorDomain error -1

Saving a Network Image to an iOS Camera Roll in React Native

Written and Developed By: @Phat_Ca, @caitlyn, @craig

Intro

In this application with React Native we tried to save an online image into the iOS photo gallery. This thread builds on this conversation as part of a larger series to test React Native on the theta-plugin application using iOS.

Testing Environment

  • IOS version 17.5
  • Xcode version 15.4
  • React Native version 0.75.2

Packages

View the full code here: Github Example with React Native blob util

Overall Workflow

  • Initial setup:

    • Ensure that you have Node.js, Watchman and Xcode installed on your device
    • CocoaPods (required for managing native dependencies)
  • Creating new react native project:

    • Run command npx @react-native init YourAppName
    • cd into ios directory and run pod install
    • To start your app run npx react-native run-ios
  • Handling module or packages:

    • Example permission package: $ npm install --save react-native-permissions
    • cd into ios and run pod install
    • Then cd … and rebuild app run npx react-native run-ios
    • Sometimes handling native module require linking with command npx react-native link package-name
  • Developing the App:

    • Download Image: implement the function to download image to local storage using react-native-fs or react-native-blob-util
    • Check permission using react-native-permissions to handle request and check
    • Save image to gallery using camera roll

Challenges/Solutions

  • @react-native-camera-roll/camera-roll plugin Issues

    • Challenge: In previous tutorials we referenced, the plugin for camera roll was deprecated. The newer version of camera roll referenced here also had minor deprecation issues. The methods save and savePicture showed as deprecated methods in the newer version of the plugin.
    • Solution: Use the method saveAsset from the @react-native-camera-roll/camera-roll plugin version 7.8.3
    • Challenge: We came across a bug in the plugin when trying to use the saveAsset method. The error PHPhotosErrorDomain error -1 kept appearing when attempting to save the image.

    • Solution: The solution linked here mentions the plugin @react-native-permissions. Install the plugin and follow the necessary steps to implement the plugin listed in the documentation. KEY POINT: check the permissions with @react-native-permissions right before calling the function saveAsset from the CameraRoll plugin. The key permission to check is PERMISSIONS.IOS.PHOTO_LIBRARY
  • @react-native-permissions plugin issues

    • Challenge: After installing the @react-native-permissions plugin, we received the No Permission Handler detected error.
      • Solution: Thoroughly read through the documentation for the plugin linked here. Make sure to edit the Podfile and execute pod install. Then update the Info.plist with the permissions. Run the command npx react-native run-ios` to build the project afterward.
      • If you still see the error, you may need to clean the XCode Derived Data. XCode → Product → Clean Project. Then, close the IOS Simulator and run pod install again. Re-run your app.
  • Troubleshooting Tips:

    • Run the project from XCode itself if the project fails to build on terminal. This will reveal more errors
    • Ensure that you have a simulator properly configured and installed in XCode for the project.
  • Permission for photo library info.plist to grant access for application to gallery

  • Additional Information in ios setting if application is set for Add Photos only error occur and would not save image to photo gallery but limited access and full access works fine

2 Likes

I am downloading the video from an ip camera which is downloaded to DocumentDir , and then trying to save in photos (which is working fine on android) but Ios shows error:

“The operation couldn’t be completed (PHPhotosErrorDomain error 3302)”

This is my code to download the video:

const handleDownload = async filePath => {
const videoPath = filePath.split('A:')[1].replace(//g, '/');
const vUrl = http://${selectedCamera.ip}/${videoPath};
const {config, fs} = RNFetchBlob;
const downloads = Platform.OS === 'ios' ? fs.dirs.DocumentDir : fs.dirs.DownloadDir;

const tsFilePath = `${downloads}/${videoPath.split('/').pop()}`;
const mp4FilePath = tsFilePath.replace('.TS', '.mp4');

// Check if the video is already downloaded
const storedDownloads =
JSON.parse(await AsyncStorage.getItem('downloads')) || [];
const isAlreadyDownloaded = storedDownloads.some(
video => video.filePath === mp4FilePath,
);

if (isAlreadyDownloaded) {
Alert.alert(
'Already Downloaded',
'This video has already been downloaded.',
);
return; // Exit the function early
}

// If not downloaded, proceed with download
setTimeout(() => dispatch(DownloadLoader()), 700);

try {
const response = await config({
fileCache: true,
path: tsFilePath,
}).fetch('GET', vUrl)
.progress((received, total) => {
const progress = received / total;
setDownloadProgress(progress); // Update progress
});

await FFmpegKit.execute(`-i ${tsFilePath} -c copy ${mp4FilePath}`);

await fs.unlink(tsFilePath);

/*saving the file in albums */

let savedUri = null;
try {
savedUri = await CameraRoll.save(mp4FilePath, {type: 'video'});
Alert.alert('Saved to Photos ', 'Video has been saved to your Phone Album');
console.log(savedUri);
} catch (saveError) {
Alert.alert(
'Save Failed',
`Failed to save video to Photos: ${saveError.message}`,
);
}

const downloadedVideo = {
name: mp4FilePath.split('/').pop(),
filePath: savedUri,
url: vUrl,
};
dispatch(addToDownloads(downloadedVideo));

storedDownloads.push(downloadedVideo);
await AsyncStorage.setItem('downloads', JSON.stringify(storedDownloads));

console.log(downloadedVideo);

} catch (error) {
Alert.alert('Download Failed', `Error: ${error.message}`);
} finally {
setTimeout(() => dispatch(DownloadLoader(false)), 700);
}
};

issue is not with file extension , it is (.mp4) , checked on android as well
I have given necessary permissions also in info.plist , still I am receiving this error on ios
any solutions? for this

packages:
"react-native": "0.74.2",
"rn-fetch-blob": "^0.12.0",
"@react-native-camera-roll/camera-roll": "^7.8.3",

I believe there is a bug in the camera-roll package, not necessarily your code. We needed to check the ios permission immediately prior to saving the file. There is likely a better solution that manages the threads better. However, it seems to work if you check the permissions of the camera roll for write access immediately before calling the method to save to file and write.