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.
I think you were on the right lines
PHAsset.fetchAssets(with: fetchOptions)as this will allow the return of multiple media types. You might even be able to use it without any predicate at all, but if you want to specifically return just videos and images this works on my quick test:fetchResult.countis different depending if I just use.video,.imageor both in the query, with the numbers feeling appropriate for my photo library.