SWIFT: Crash when trying to fetch all Images From Library - swift

The app keeps crashing when I attempt to fetch all images from library, the message from debugger is: Terminated due to memory issue
Any idea how can fix it? Here is my code:
func fetchImagesFromLibrary() {
let fetchOptions: PHFetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
fetchResult.enumerateObjects { (object, index, stop) -> Void in
let options = PHImageRequestOptions()
options.isSynchronous = false
options.deliveryMode = .fastFormat
PHImageManager.default().requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: self.view.frame.size, contentMode: PHImageContentMode.aspectFill, options: .none, resultHandler: { (image, _) in
if let image = image {
print(image)
//self.imageview1.image = image
}
}
)
}
}

Related

Select Multiple Videos and Convert PHAsset to Video File using OpalImagePickerController in swift

I am working with OpalImagePickerController in Swift dealing with uploading multiple images and videos. I can easily deal with images and converting to PHAsset to UIImage but I am trying to upload multiple videos with OpalImagePickerController but unable to convert PHAsset to Video format to upload. I am providing my code .
let imagePicker = OpalImagePickerController()
imagePicker.imagePickerDelegate = self
imagePicker.allowedMediaTypes = Set([PHAssetMediaType.image])
imagePicker.maximumSelectionsAllowed = 20
self.present(imagePicker, animated: true, completion: nil)
internal func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset])
{
var image = UIImage()
for asset in assets
{
switch asset.mediaType {
case .image:
print("Image")
image = asset.getAssetThumbnail()
let data = image.pngData() as NSData?
self.sendImage(data:data! as Data, previewImage: image)
case .video:
print("Video")
let options: PHVideoRequestOptions = PHVideoRequestOptions ()
options.deliveryMode = .highQualityFormat
options.version = .original
PHImageManager.default().requestAVAsset (forVideo: asset, options: options, resultHandler: {(asset, audioMix, info) in
if let urlAsset = asset as? AVURLAsset {
let playerItem = AVPlayerItem(asset: urlAsset)
self.sendVideo(video: playerItem, isFromCamera: false)
} else {
}
})
case .audio:
print("Audio")
default:
print("Unknown")
}
}
picker.dismiss(animated: true, completion: nil)
}
extension PHAsset {
func getAssetThumbnail() -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: self,
targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight),
contentMode: .aspectFit,
options: option,
resultHandler: {(result, info) -> Void in
thumbnail = result!
})
return thumbnail
}
Please help me to convert PHAsset to Video format

Save Image to Photo Library and retrieve the URL

I can save an image to my photo library using the following code:
PHPhotoLibrary.shared().performChanges({
PHAssetCreationRequest
.creationRequestForAssetFromImage(atFileURL: outfileURL)
}) { (saved, err) in
print("Saved?", saved)
if (saved) {
DispatchQueue.main.async {
onComplete(outfileURL.absoluteString)
}
}
}
But I am trying to load the image I just saved in an Image (SwiftUI) and I want to get the new images fileURL. The outfileURL is a temp file and is not retained. Please note this is a gif - if that has any bearing.
I am trying to use the PHObjectPlaceholder thing but I still don't know how to get the location out:
var placeHolder: PHObjectPlaceholder? = nil
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHAssetCreationRequest.creationRequestForAssetFromImage(atFileURL: outfileURL)
placeHolder = changeRequest?.placeholderForCreatedAsset
}) { (saved, err) in
print("Saved? \(saved) to location \(placeHolder?)") //<--- AAAARGH!!!!
if (saved) {
DispatchQueue.main.async {
onComplete(/*????*/)
}
}
}
From PHObjectPlaceholder you can use localIdentifier
let fetchOptions = PHFetchOptions()
let fetchResult: PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [placeholder.localIdentifier], options: fetchOptions)
if let asset = fetchResult.firstObject {
// Here you can get UIImage from PHAsset
}
So this is possible solution:
var placeHolder: PHObjectPlaceholder? = nil
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHAssetCreationRequest.creationRequestForAssetFromImage(atFileURL: outfileURL)
placeHolder = changeRequest?.placeholderForCreatedAsset
}) { (saved, err) in
if let localIdentifier = placeHolder?.localIdentifier, saved {
let fetchOptions = PHFetchOptions()
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: fetchOptions)
if let phAsset = fetchResult.firstObject {
let targetSize = CGSize(width: CGFloat(phAsset.pixelWidth), height: CGFloat(phAsset.pixelHeight))
let options = PHImageRequestOptions()
PHCachingImageManager.default().requestImage(for: phAsset, targetSize: targetSize, contentMode: .aspectFill, options: options) { (uiImage, info) in
DispatchQueue.main.async {
onComplete(uiImage)
}
}
}
}
}

PhotoKit - get thumbnail image and video data

I'd like to make a list view of videos which saved in user's Photos App.
So, I'm trying to make an array containing thumbnail image and AVAsset(or duration and fileURL).
But in the following code, I'm wondering if I can't make the array because of the multiple blocks.
The reason I wrote to blocks was I wanted to get both the thumbnail image and the video data(duration and fileURL).
Is there a way to make such an array?
PhotoKit Fetch Request
var videos = [Video]()
let imageManager = PHImageManager.default()
let fetchOptions = PHFetchOptions()
let imageRequestOptions = PHImageRequestOptions()
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions)
fetchResult.enumerateObjects { (phAsset, _, _) in
var video = Video()
imageManager.requestImage(for: phAsset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFit, options: imageRequestOptions) { (uiImage, _) in
video.thumbnailImage = uiImage!
self.videos.append(video)
}
imageManager.requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
if avAsset != nil {
video.asset = avAsset!
}
}
}
Array's Element
struct Video {
var thumbnailImage: UIImage?
var asset: AVAsset?
}
Just nest the calls:
var video = Video()
imageManager.requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
if avAsset != nil {
video.asset = avAsset!
}
imageManager.requestImage(for: phAsset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFit, options: imageRequestOptions) { (uiImage, _) in
video.thumbnailImage = uiImage!
self.videos.append(video)
}
}

Get an error when trying to get all the photos from PHAssetCollection.fetchAssetCollections

I want to get all the photos of my custom album.
but instead what I get is the below error.
My Code
let collections:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
Error i get
"Error returned from daemon: Error Domain=com.apple.accounts Code=7
"(null)""
Any ideas on how to fix this?
Based on the comments, I'm not entirely sure what the issue is but I hope this code could provide some assistance. Using .album rather than .smartAlbum could also be part of the issue.
private var fetchResult: PHFetchResult<PHAsset>!
func fetchOptions(_ predicate: NSPredicate?) -> PHFetchOptions {
let options = PHFetchOptions()
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ]
options.predicate = predicate
return options
}
}
if let userLibraryCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil).firstObject {
self.fetchResult = PHAsset.fetchAssets(in: userLibraryCollection, options: fetchOptions(NSPredicate(format: "mediaType = \(PHAssetMediaType.image.rawValue)")))
} else {
self.fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions(nil))
}
private var fetchResult: PHFetchResult<PHAsset>!
func picker(_ picker: PHPickerViewController, didFinishPicking
results: [PHPickerResult]) {
self.dismiss(animated: true, completion: nil)
if let userLibraryCollection =
PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype:
.smartAlbumUserLibrary, options: nil).firstObject {
self.fetchResult = PHAsset.fetchAssets(in:
userLibraryCollection, options: fetchOptions(NSPredicate(format:
"mediaType = \(PHAssetMediaType.image.rawValue)")))
fetchResult.enumerateObjects { asset, index, stop in
PHAsset.getURL(ofPhotoWith: asset) { (url) in
if let imgurl = url{
print(imgurl)
}else {
print("error")
}
}
}
} else {
self.fetchResult = PHAsset.fetchAssets(with: .image, options:
fetchOptions(nil))
print(fetchResult.firstObject)
}
}
extension PHAsset {
static func getURL(ofPhotoWith mPhasset: PHAsset, completionHandler : #escaping ((_ responseURL : URL?) -> Void)) {
if mPhasset.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
mPhasset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
if let fullSizeImageUrl = contentEditingInput?.fullSizeImageURL {
completionHandler(fullSizeImageUrl)
} else {
completionHandler(nil)
}
})
} else if mPhasset.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: mPhasset, options: options, resultHandler: { (asset, audioMix, info) in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl = urlAsset.url
completionHandler(localVideoUrl)
} else {
completionHandler(nil)
}
})
}
}
}
Do everything with PHAsset on the main thread. I still get that error, but at least images are fetched.
let options = PHFetchOptions()
options.includeHiddenAssets = false
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] // Newest first
options.predicate = NSPredicate(format: "mediaType = \(PHAssetMediaType.image.rawValue)") // Only images
DispatchQueue.main.async {
self.mediaAssets = PHAsset.fetchAssets(with: options)
}

Issues with read file permission Swift & Firebase - mobile not simulator

I am attempting to upload a video from a custom UICollection that accesses the users videos. When I attempt to upload the URL to firebase, I get the following error:
Body file is unreachable: /var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3002.MOV” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV, NSUnderlyingError=0x17024b790 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I am a little confused as to why this is occuring, b/c when I print out the url of the video I get:
file:///var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV
&& my info.plist looks like this
Here is the upload code:
videosFolder.child("\(nsuid).mov").putFile(self.videoURLToUpload, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("we Had an Error!: \(String(describing: error))")
} else {
print("we uploaded the video correctly!!!")
}
})
&& just in-case here is the where I am fetching the videos
struct Media {
var image:UIImage?
var videoURL:NSURL?
}
var mediaArray = [Media]()
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
var mediaItem = Media()
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 400, height: 400), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
mediaItem.image = imageOfVideo;
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
let videoData = NSURL(string: "\(asset.url)")
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
mediaItem.videoURL = videoData!
self.mediaArray.append(mediaItem)
print(self.mediaArray.count)
}
})
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
This issue is on the mobile not the simulator