ivpusic / react-native-image-crop-picker

iOS/Android image picker with support for camera, video, configurable compression, multiple images and cropping
MIT License
6.16k stars 1.56k forks source link

Failure to select some photos whose originals are on iCloud: likely Apple bug #818

Open chrisbobbe opened 6 years ago

chrisbobbe commented 6 years ago

Version

Tell us which versions you are using:

Platform

Expected behaviour

After selecting a locally stored, compressed version of an image whose original exists in iCloud, I should either get the data for that image or receive a more specific error message.

Actual behaviour

E_CANNOT_SAVE_IMAGE is thrown with the message "Cannot save image. Unable to write to tmp location."

Suspected cause is a bug in Apple's iCloud Photo Sharing; please see below.

Steps to reproduce

  1. Open image picker

  2. Select a photo that is the locally stored, compressed version of an original that exists in iCloud

  3. Observe error

Attachments

When requestImageDataForAsset is called, I'm seeing this log:

[Generic] Failed to load image data for asset <PHAsset: 0x13d041940> 87CCAFDC-A0E3-4AC9-AD1C-3F57B897A52E/L0/001 mediaType=1/0, sourceType=2, (113x124), creationDate=2015-06-29 04:56:34 +0000, location=0, hidden=0, favorite=0 with format 9999

So [data writeToFile:filePath atomically:YES] doesn't work, persistFile returns nil, and the E_CANNOT_SAVE_IMAGE error is thrown.

user2067021 does a good job of describing the problem at this Stack Overflow page, and I believe they are correct that it is caused by a bug in iCloud Photo Sharing: https://stackoverflow.com/questions/28170444/photos-framework-requestimagedataforasset-occasionally-fails. Note that their answer was not marked as the accepted answer at the time I posted this; it can be found directly below the accepted answer. They have submitted a bug report to Apple.

One solution is to throw a more specific error that references the bug, or at least the fact that the photo's original is stored in iCloud. I'm not sure how specific that error should be, as users may be scared if they receive a call-to-action saying their iCloud photo cache is corrupted and they need to reset it.

Or, there may be a solution that hasn't occurred to me that would allow access to either the compressed photo or the original photo.

superandrew213 commented 6 years ago

@chrisbobbe can you try enabling all the albums here:

https://github.com/ivpusic/react-native-image-crop-picker/blob/master/ios/src/ImageCropPicker.m#L284-L311

chrisbobbe commented 6 years ago

@superandrew213 Yes, I added this to the options passed toImageCropPicker.openPicker(options):

        smartAlbums: [
            'Regular',
            'SyncedEvent',
            'SyncedFaces',
            'SyncedAlbum',
            'Imported',
            'PhotoStream',
            'CloudShared',
            'Generic',
            'Panoramas',
            'Videos',
            'Favorites',
            'Timelapses',
            'AllHidden',
            'RecentlyAdded',
            'Bursts',
            'SlomoVideos',
            'UserLibrary',
            'SelfPortraits',
            'Screenshots',
            'DepthEffect',
            'LivePhotos',
            'Animated',
            'LongExposure'
        ]

The photos that were giving me the problem were still visible in the picker, and the error still happened when I tried to pick them. It seems like this option only filters which photos are visible in the picker, and doesn't fix the error that gets thrown when you try to select some specific photos.

I did try filtering the photos by commenting out some of the albums in this list -- it would be great for my use case if I could just remove the photos that caused the error, and show the rest -- but I couldn't isolate the problematic photos to a single album. I thought, for example, I could exclude 'CloudShared' and allow all the other albums, but the problematic photos were not filtered out. I have 1000 photos in the 'PhotoStream' album, and I saw that some of them cause the problem and some do not, so I can't filter by that album.

chrisbobbe commented 4 years ago

It would've been helpful if I recorded my iOS version in this issue, now, wouldn't it?! I'm sorry about that. And I can't find what version I was on on Sept 7, 2018.

Helpfully, https://stackoverflow.com/a/48697434/6792075 (the comment I linked to above, which I believe explains the cause of this issue) mentioned that they first observed this issue on an upgrade from iOS 10.x to 11.0.3, and it was seen through 11.2.5. It's unknown if later versions are also affected (and they may not be, as this issue has remained open without much activity).

antonandreyev commented 4 years ago

Hi, we experience this issue but in specific conditions. RN 0.61.5 react-native-image-crop-picker 0.31.1 iOS 13.3.1 and 13.4.1 Users have enabled "Optimize iPhone Storage" (i.e. uploading photos to iCloud).

In Online it's ok. But when user goes Offline and tries to pick up a picture - it gives a error "Unable to write to tmp location.". Strange thing is that it happens not on all phones. We have two users with iPhone X who don't experience this issue (or at least not all the time). But for iPhone 8 and iPhone 11 Pro Max user it happens all the time. It affects only iClouded pictures. Locally stored pics are always fine.

Once again - we see the error only when the phone is in offline and not on every device.

Since it's iOS 13 we cannot even use sourceURL property since it's null (see related issue).

Before you ask - yes, we need it working offline. Any advices in our situation?

antonandreyev commented 4 years ago

A bit of offtopic (sorry) but just in case somebody else experiences the same issues. We use the following workaround for offline using react-native-phasset library:

let attData = await ImagePicker.openPicker({
    multiple: true,
    compressVideoPreset: 'HighestQuality',
    mediaType: Platform.OS === 'ios' && !online ? 'photos' : 'any', //does not disable videos!
    smartAlbums: online ? ['UserLibrary', 'PhotoStream', 'Panoramas', 'Videos','Bursts']
                : ['UserLibrary', 'PhotoStream', 'Panoramas'], //does not disable videos!
    writeTempFile: !(Platform.OS === 'ios' && !online),
    cropping: Platform.OS === 'ios' && !online, //hack to actually disable videos
});

let attachments = [];
for (let item of attData) {
    let uri = item.path || item.sourceURL;
    if (Platform.OS === 'ios' && !uri && item.localIdenfifier) { //because on some phone models it works fine
        try {
            const item1 = await requestImage({
                id: item.localIdenfifier,
            });
            uri = item1.path;
        } catch (e) {
            console.log('failed retrieving preview photo', e);
        }
    }
    attachments.push({
        name: item.filename,
        localIdenfifier: item.localIdentifier,
        uri: uri,
        fileType: item.mime.includes('video') ? 1 : 0,
        mime: item.mime,
    });
}

Then, when back online:

//...
let uri = attachment.uri;
let name = attachment.name;
let mime = attachment.mime;
if (Platform.OS === 'ios' && !attachment.uri && attachment.localIdenfifier) {
    const item = await Phasset.requestImage({
        id: attachment.localIdenfifier,
    });
    uri = item.path;
    name = item.filename;
    mime = item.mime;
}
//...

Code might be not very polished. We are disabling videos because when we select them the app just hangs up on "Processing assets" (see this issue). Also, when we fail to fetch preview (uri/path is emtpy) we just show some placeholder.

It doesn't mean the original issue is resolved. Since this seems to be an OS limitation as a possible solution we'd like to still receive localIdentifier when "Unable to write to tmp location" appears, not the exception.

PauliusLiekis commented 3 years ago

I'm experiencing a similar issue. I have two flavors of it:

1) If I select one photo, then I simply get an error:

Error: Cannot save image. Unable to write to tmp location.
    at Object.promiseMethodWrapper [as openPicker] (NativeModules.js:103)
    at chooseImages$ (Save.tsx:124)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:293)
    at Generator.next (runtime.js:118)
    at tryCatch (runtime.js:63)
    at invoke (runtime.js:154)
    at runtime.js:164
    at tryCallOne (core.js:37)
    at core.js:123

2) If I select many photos then I don't get an exception on JS side and it simply hangs the whole app. There is stuff like this in the XCode log:

2021-02-02 14:11:28.745441+0200 FATMAP[1412:204937] Error: Download required, request again with network access allowed
2021-02-02 14:11:28.746748+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.747216+0200 FATMAP[1412:204937] Error: Download required, request again with network access allowed
2021-02-02 14:11:28.751053+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.768255+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.768731+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"

As others have pointed out - it seems to have something to do with iCloud photos. In my case, I think I have (optimized) photos on the device, but the iCloud account is disconnected. See one of the photos marked with the exclamation mark in the Photos app: IMG_6584

lunajing commented 3 years ago

@PauliusLiekis Have you solved it ?

PauliusLiekis commented 3 years ago

@lunajing nah. We decided that we can live it for now as not too many users will run into the issue that I had... unless there much more use-cases how this can be reproduced...

Having said that, I would still like to see it fixed...

lucasftcruz commented 3 years ago

Is there any ETA on when this issue will be fixed?

truongngodang commented 3 years ago

I'm experiencing a similar issue. I have two flavors of it:

  1. If I select one photo, then I simply get an error:
Error: Cannot save image. Unable to write to tmp location.
    at Object.promiseMethodWrapper [as openPicker] (NativeModules.js:103)
    at chooseImages$ (Save.tsx:124)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:293)
    at Generator.next (runtime.js:118)
    at tryCatch (runtime.js:63)
    at invoke (runtime.js:154)
    at runtime.js:164
    at tryCallOne (core.js:37)
    at core.js:123
  1. If I select many photos then I don't get an exception on JS side and it simply hangs the whole app. There is stuff like this in the XCode log:
2021-02-02 14:11:28.745441+0200 FATMAP[1412:204937] Error: Download required, request again with network access allowed
2021-02-02 14:11:28.746748+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.747216+0200 FATMAP[1412:204937] Error: Download required, request again with network access allowed
2021-02-02 14:11:28.751053+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.768255+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"
2021-02-02 14:11:28.768731+0200 FATMAP[1412:204633] [ImageManager] Media resource request failed to return valid data or url with error: Error Domain=PHPhotosErrorDomain Code=-1 "(null)"

As others have pointed out - it seems to have something to do with iCloud photos. In my case, I think I have (optimized) photos on the device, but the iCloud account is disconnected. See one of the photos marked with the exclamation mark in the Photos app: IMG_6584

me too, I think when select many items should throw error, my phone frozen when select many items (Cannot save image. Unable to write to tmp location)

ethoman commented 2 years ago

Any updates on this? Running into the same thing in 2022

rahulchauhan9 commented 2 years ago

This issue goes away when you turn off Optimize iPhone Storage in your settings.

It happens when you are trying to select pictures that are on iCloud and not actually on your device, in offline mode you see a thumbnail of them, but they are not actually stored on your device. Those photos aren't accessible when you are offline and that's why you get the error. _E_CANNOT_SAVEIMAGE is thrown with the message "Cannot save image. Unable to write to tmp location."

We cannot ask all users to do this workaround, but this issue is most likely from apple and not the library code.

Lakston commented 2 months ago

Can someone explain how to simulate this case on an ios simulator ?

How do you force an image to be in "preview" version locally with the full res image only on icloud ?

I would like to test this, having the preview only and selecting the image, but if my icloud is synced I'll the full res locally right ?