I'm building an app for cleaning users' photo libraries, but I've hit a road-block when it comes to accessing videos. Here's the code for accessing and editing photos:
import Foundation
import Photos
class PhotosModel: ObservableObject {
@Published var errorString : String = ""
@Published var photoArray: [BetterUIImage] = []
private var currentIndex = 0
private var totalInitialPhotos = 0
private var photosToDelete = [PHAsset]()
init() {
PHPhotoLibrary.requestAuthorization { (status) in
DispatchQueue.main.async {
switch status {
case .authorized, .limited:
self.errorString = ""
self.getInitialPhotos()
case .denied, .restricted:
self.errorString = "Photo access permission denied"
case .notDetermined:
self.errorString = "Photo access permission not determined"
@unknown default:
fatalError()
}
}
}
}
fileprivate func getInitialPhotos() {
let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let results: PHFetchResult = PHAsset.fetchAssets(with: fetchOptions)
totalInitialPhotos = results.count
if results.count > 2 {
for i in 0..<3 {
let asset = results.object(at: i)
let size = CGSize(width: 700, height: 700) //You can change size here
manager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: requestOptions) { (image, _) in
DispatchQueue.main.async {
if let image = image {
let betterImage = BetterUIImage(image: image, asset: asset)
self.photoArray.append(betterImage)
} else {
print("error asset to image")
}
}
}
}
currentIndex = 3
} else if results.count > 0 {
for i in 0..<results.count {
let asset = results.object(at: i)
let size = CGSize(width: 700, height: 700) //You can change size here
manager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: requestOptions) { (image, _) in
DispatchQueue.main.async {
if let image = image {
let betterImage = BetterUIImage(image: image, asset: asset)
self.photoArray.append(betterImage)
} else {
print("error asset to image")
}
}
}
}
} else {
DispatchQueue.main.async {
self.errorString = "No photos to display"
}
}
self.photoArray = self.photoArray.reversed()
print("getAllPhotos() completed")
}
func appendToPhotosToDelete(_ asset: PHAsset) {
photosToDelete.append(asset)
print("appendToPhotosToDelete() completed")
}
fileprivate func getNextPhoto() {
let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
// TODO: Set change with .image to with fetchOptions: Gets all types
let results: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
// TODO: Change photoArray so that it imcludes all three types
self.photoArray = photoArray.shiftRight()
let asset = results.object(at: currentIndex)
let size = CGSize(width: 700, height: 700) //You can change size here
// TODO: Make this so that it applies to Images, Lives, and Videos
manager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: requestOptions) { (image, _) in
DispatchQueue.main.async {
if let image = image {
self.photoArray.removeLast()
self.photoArray.append(BetterUIImage(image: image, asset: asset))
} else {
print("error asset to image")
}
}
}
}
func next() {
if currentIndex == totalInitialPhotos {
self.photoArray = []
return
} else if currentIndex == totalInitialPhotos - 1 {
self.photoArray.removeLast()
} else if currentIndex == totalInitialPhotos - 2 {
self.photoArray.removeLast()
}
getNextPhoto()
currentIndex += 1
print(currentIndex)
}
func deletePhoto() {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets(NSArray(array: self.photosToDelete))
})
}
}
Is there a way to edit this system in order to return both images and videos? I tried by changing the PHAsset.fetchAssets(with: .image, options: fetchOptions) call to HAsset.fetchAssets(with: fetchOptions). That didn't seem to work, but also didn't break anything.
Is there another way to do this? I've seen other apps do something similar but all of the information I can find online involves PhotosPicker, which pretty much defeats the purpose of my app.
Edit: Remove unnecessary UIKit import.
PHAsset.fetchAssets(with: fetchOptions
and add a predicate to the fetchOptions that matchesmediaType = .image || mediaType = .video
would be the way to go. Will have a play when I'm on the Mac.