import Foundation import Photos import UIKit final class FileProcessing { static let shared = FileProcessing() static var fileFolder: URL { // swiftlint:disable:next force_unwrapping let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let subdirectoryURL = documentsURL.appendingPathComponent(Const.fileFolder) if !FileManager.default.fileExists(atPath: subdirectoryURL.path) { try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil) } return subdirectoryURL } func createThumbnail(localName: String) -> String? { let thumbnailFileName = "thumb_\(localName)" let thumbnailUrl = FileProcessing.fileFolder.appendingPathComponent(thumbnailFileName) let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) // check if thumbnail already exists if FileManager.default.fileExists(atPath: thumbnailUrl.path) { return thumbnailFileName } // create thumbnail if not exists switch localName.attachmentType { case .image: guard let image = UIImage(contentsOfFile: localUrl.path) else { return nil } let targetSize = CGSize(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) guard let thumbnail = scaleAndCropImage(image, targetSize) else { return nil } guard let data = thumbnail.pngData() else { return nil } do { try data.write(to: thumbnailUrl) return thumbnailFileName } catch { return nil } default: return nil } } func fetchGallery() -> [SharingGalleryItem] { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: fetchOptions) var items: [SharingGalleryItem] = [] assets.enumerateObjects { asset, _, _ in if asset.mediaType == .image { items.append(.init(id: asset.localIdentifier, type: .photo)) } else if asset.mediaType == .video { items.append(.init(id: asset.localIdentifier, type: .video)) } } return items } func fillGalleryItemsThumbnails(items: [SharingGalleryItem]) -> [SharingGalleryItem] { var result: [SharingGalleryItem] = [] let ids = items .filter { $0.thumbnail == nil } .map { $0.id } let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil) assets.enumerateObjects { asset, _, _ in if asset.mediaType == .image { PHImageManager.default().requestImage( for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: nil ) { image, _ in image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in if let image { let data = image.jpegData(compressionQuality: 1.0) ?? Data() result.append(.init(id: asset.localIdentifier, type: .photo, thumbnail: data)) } } } } else if asset.mediaType == .video { PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in if let avAsset { let imageGenerator = AVAssetImageGenerator(asset: avAsset) imageGenerator.appliesPreferredTrackTransform = true let time = CMTimeMake(value: 1, timescale: 2) do { let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil) let thumbnail = UIImage(cgImage: imageRef) thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in if let image { let data = image.jpegData(compressionQuality: 1.0) ?? Data() result.append(.init(id: asset.localIdentifier, type: .video, thumbnail: data)) } } } catch { print("Failed to create thumbnail image") } } } } } return result } } private extension FileProcessing { func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? { let aspect = img.size.width / img.size.height let targetAspect = size.width / size.height var newWidth: CGFloat var newHeight: CGFloat if aspect < targetAspect { newWidth = size.width newHeight = size.width / aspect } else { newHeight = size.height newWidth = size.height * aspect } UIGraphicsBeginImageContextWithOptions(size, false, 0.0) img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } func syncEnumrate(_ ids: [String]? = nil) -> [PHAsset] { var result: [PHAsset] = [] let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] if let ids { fetchOptions.predicate = NSPredicate(format: "localIdentifier IN %@", ids) } let assets = PHAsset.fetchAssets(with: fetchOptions) assets.enumerateObjects { asset, _, _ in result.append(asset) } return result } }