#if !os(tvOS)
import Foundation
import Photos
/// Distinguishes a Photos-permission denial (recoverable via Settings) from a
/// write failure (transient).
enum CaptureSaveError: Error, Equatable {
case permissionDenied
case writeFailed
}
enum CaptureSaver {
/// Save a still/clip file URL to the Photos library (add-only). Requests
/// permission on first use.
static func saveToPhotos(_ url: URL, isVideo: Bool) async -> Result<Void, CaptureSaveError> {
let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly)
guard status == .authorized || status == .limited else { return .failure(.permissionDenied) }
let ok = await withCheckedContinuation { cont in
PHPhotoLibrary.shared().performChanges {
let req = PHAssetCreationRequest.forAsset()
let opts = PHAssetResourceCreationOptions()
opts.originalFilename = url.lastPathComponent
req.addResource(with: isVideo ? .video : .photo, fileURL: url, options: opts)
} completionHandler: { ok, _ in cont.resume(returning: ok) }
}
return ok ? .success(()) : .failure(.writeFailed)
}
/// Save a Live Photo (paired HEIC still + MOV) to the Photos library.
static func saveLivePhoto(still: URL, video: URL) async -> Result<Void, CaptureSaveError> {
let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly)
guard status == .authorized || status == .limited else { return .failure(.permissionDenied) }
let ok = await withCheckedContinuation { cont in
PHPhotoLibrary.shared().performChanges {
let req = PHAssetCreationRequest.forAsset()
req.addResource(with: .photo, fileURL: still, options: nil)
req.addResource(with: .pairedVideo, fileURL: video, options: nil)
} completionHandler: { ok, _ in cont.resume(returning: ok) }
}
return ok ? .success(()) : .failure(.writeFailed)
}
}
#endif