Files
Schnappix/node_modules/expo-media-library/ios/next/repository/AssetRepository.swift
T

130 lines
4.3 KiB
Swift

import Photos
final class AssetRepository {
static let shared = AssetRepository()
private init() {}
func delete(by ids: [String]) async throws {
let assets = get(by: ids)
try await delete(by: assets)
}
func delete(by assets: [PHAsset]) async throws {
try await PHPhotoLibrary.shared().performChanges {
PHAssetChangeRequest.deleteAssets(assets as NSFastEnumeration)
}
}
func get(by ids: [String]) -> [PHAsset] {
var assets: [PHAsset] = []
let fetchedAssets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
fetchedAssets.enumerateObjects { asset, _, _ in
assets.append(asset)
}
return assets
}
func get(by collection: PHAssetCollection) -> [PHAsset] {
var assets: [PHAsset] = []
let fetchedAssets = PHAsset.fetchAssets(in: collection, options: nil)
fetchedAssets.enumerateObjects { asset, _, _ in
assets.append(asset)
}
return assets
}
func get(by collections: [PHAssetCollection]) -> [PHAsset] {
var assets: [PHAsset] = []
for collection in collections {
assets.append(contentsOf: get(by: collection))
}
return assets
}
func get(with options: PHFetchOptions) -> [PHAsset] {
var assets: [PHAsset] = []
let fetchedAssets = PHAsset.fetchAssets(with: options)
fetchedAssets.enumerateObjects { asset, _, _ in
assets.append(asset)
}
return assets
}
func get(by collection: PHAssetCollection, with options: PHFetchOptions) -> [PHAsset] {
var assets: [PHAsset] = []
let fetchedAssets = PHAsset.fetchAssets(in: collection, options: options)
fetchedAssets.enumerateObjects { asset, _, _ in
assets.append(asset)
}
return assets
}
func add(from filePaths: [URL]) async throws -> [String] {
var assetIds: [String] = []
try await PHPhotoLibrary.shared().performChanges {
assetIds = try filePaths.map { filePath in
let creationRequest = try self.makeCreationRequest(for: filePath)
return try self.extractIdentifier(from: creationRequest)
}
}
return assetIds
}
func add(from filePath: URL) async throws -> String {
guard let firstId = try await add(from: [filePath]).first else {
throw FailedToCreateAssetException("Failed to create asset from file \(filePath.lastPathComponent)")
}
return firstId
}
private func makeCreationRequest(for filePath: URL) throws -> PHAssetChangeRequest {
let mediaType = detectMediaType(for: filePath)
try ensureMediaTypeIsSupported(mediaType, filePath: filePath)
return try createAssetChangeRequest(for: filePath, mediaType: mediaType)
}
private func detectMediaType(for filePath: URL) -> PHAssetMediaType {
assetType(for: filePath)
}
private func ensureMediaTypeIsSupported(_ type: PHAssetMediaType, filePath: URL) throws {
guard !filePath.pathExtension.isEmpty else {
throw FailedToCreateAssetException("File '\(filePath.lastPathComponent)' has no extension")
}
guard type != .unknown else {
throw FailedToCreateAssetException("File '\(filePath.lastPathComponent)' has an unknown media type")
}
guard type != .audio else {
throw FailedToCreateAssetException("Audio files are not supported: \(filePath.lastPathComponent)")
}
}
private func createAssetChangeRequest(for filePath: URL, mediaType: PHAssetMediaType) throws -> PHAssetChangeRequest {
switch mediaType {
case .video:
guard let request = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: filePath) else {
throw FailedToCreateAssetException("Unable to create video asset request for \(filePath.lastPathComponent)")
}
return request
case .image:
guard let request = PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: filePath) else {
throw FailedToCreateAssetException("Unable to create image asset request for \(filePath.lastPathComponent)")
}
return request
default:
throw FailedToCreateAssetException("Unsupported asset type for file: \(filePath.lastPathComponent)")
}
}
private func extractIdentifier(from request: PHAssetChangeRequest) throws -> String {
guard let placeholder = request.placeholderForCreatedAsset else {
throw FailedToCreateAssetException("Asset creation request does not contain a placeholder")
}
return placeholder.localIdentifier
}
}