Reputation: 9586
Users can download image and video files from a server which are then stored temporarily under the app's Documents path and then copied from there to a custom album in the iOS photo library with either PHAssetChangeRequest.creationRequestForAssetFromImage
or PHAssetChangeRequest.creationRequestForAssetFromVideo
.
This works flawless when only image files OR video files are downloaded but when both images and videos are mixed in the same download batch there are always some files failing arbitrarily with one of these errors:
Error was: The operation couldn’t be completed. (PHPhotosErrorDomain error -1.)
or
Error was: The operation couldn’t be completed. (PHPhotosErrorDomain error 3302.)
This is the responsible code for copying files to the photo library:
public func saveFileToPhotoLibrary(_ url: URL, completion: @escaping (Bool) -> Void)
{
guard let album = _album else
{
completion(false)
return
}
guard let supertype = url.supertype else
{
completion(false)
return
}
if supertype == .image || supertype == .movie
{
DispatchQueue.global(qos: .background).async
{
PHPhotoLibrary.shared().performChanges(
{
guard let assetChangeRequest = supertype == .image
? PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: url)
: PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url) else
{
completion(false)
return
}
guard let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset else
{
completion(false)
return
}
guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album) else
{
completion(false)
return
}
albumChangeRequest.addAssets([assetPlaceholder] as NSFastEnumeration)
})
{
saved, error in
if let error = error
{
print("Failed to save image file from \(url.lastPathComponent) to photo gallery. Error was: \(error.localizedDescription)")
DispatchQueue.main.async { completion(false) }
}
}
}
}
}
Does anyone know why the errors happen or how to solve this issue so that no file copies are failing?
Upvotes: 3
Views: 2953
Reputation: 10899
I had the -1 error because the file didn't exist at the specified path.
Upvotes: 0
Reputation: 1
The same error also I had, Things when we downloaded and created an extension(MemeType) should be a correct example if we want to save an mp4 file we should have to download the .mp4 file, if any reason mismatch then we face this type errrors. when you don't know exactly the file type I arrange code snippets to achieve it .
enum FileType {
case jpg
case mp4
case ogv
case webm
case threeGP
case unknown
}
func determineFileType(data: Data) -> FileType {
var values = [UInt8](repeating:0, count:4)
data.copyBytes(to: &values, count: 4)
if values[0] == 0xFF && values[1] == 0xD8 && values[2] == 0xFF {
return .jpg
} else if values[0] == 0x00 && values[1] == 0x00 && values[2] == 0x00 {
return .mp4 // Basic heuristic for .mp4
} else if values[0] == 0x4F && values[1] == 0x67 && values[2] == 0x67 && values[3] == 0x53 {
return .ogv
} else if values[0] == 0x1A && values[1] == 0x45 && values[2] == 0xDF && values[3] == 0xA3 {
return .webm
} else if values[0] == 0x66 && values[1] == 0x74 && values[2] == 0x79 && values[3] == 0x70 {
return .threeGP // Basic heuristic for .3gp
} else {
return .unknown
}}
private func saveDataAsVideoFile(_ data: Data, completion: @escaping (Bool, Error?) -> Void) {
let fileType = determineFileType(data: data)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath: String
switch fileType {
case .jpg:
filePath = "\(documentsPath)/tempFile.jpg"
case .mp4:
filePath = "\(documentsPath)/tempFile.mp4"
case .ogv:
filePath = "\(documentsPath)/tempFile.ogv"
case .webm:
filePath = "\(documentsPath)/tempFile.webm"
case .threeGP:
filePath = "\(documentsPath)/tempFile.3gp"
case .unknown:
completion(false, nil)
return
}
let fileUrl = URL(fileURLWithPath: filePath)
do {
try data.write(to: fileUrl)
PHPhotoLibrary.shared().performChanges({
// Since PHAssetChangeRequest doesn't support other video formats like .ogv, .webm or .3gp directly,
// you might be limited in the types of videos you can save to the Photos library.
if fileType == .jpg {
_ = PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: fileUrl)
} else if fileType == .mp4 || fileType == .threeGP {
_ = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileUrl)
} else {
completion(false, nil)
return
}
}) { success, error in
completion(success, error)
}
} catch {
print("Error saving data: \(error)")
completion(false, error)
} }
Upvotes: 0
Reputation: 70218
PHPhotosErrorDomain error 3302
means invalidResource
, see list of error codes. This happened to me because I tried to save a picture from a file in the temporary directory. It seems only specific directories can be used:
// FAILS
let imageUrl = FileManager.default.temporaryDirectory.appendingPathComponent("save-me")
// WORKS
let documentsPath = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true)[0]
let filePath = "\(documentsPath)/tempFile.jpg"
let imageUrl = URL(fileURLWithPath: filePath)
// And then
PHPhotoLibrary.shared().performChanges(
{
let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(
atFileURL: imageUrl)
[...]
You can also get other cryptic errors. For instance, 3300
means changeNotSupported
and can occur if you try to add an asset to special albums such as "Recents" (which identifies as PHAssetCollectionType.smartAlbum
+ PHAssetCollectionSubtype.smartAlbumUserLibrary
).
Upvotes: 0
Reputation: 9586
Solved it! The above code works as intended. The problem was that file types were mixed up between some video and image files due to file order and filenames provided from server. So sometimes a video might have received an image file type extension and vice versa. This seems to confuse the PHPhotoLibrary creationRequestForAssetFromImage API.
Upvotes: 0