SwiftUI: How to - ios

I am trying to upload a video to firebase Storage, to be able to do that I have to get the following:
let localFile = URL(string: "path/to/video")!
and then upload it with:
let uploadTask = riversRef.putFile(from: localFile
My problem is that I dont know how to get that path. So far I have a have a custom image / video picker, and then a function that can upload images. I just dont know how to upload Videos, for that I believe i need to get the path.
Any ideas on how to get localFile?
UPDATE:
This is my custom Picker
import SwiftUI
import PhotosUI
class ImagePickerViewModel: ObservableObject {
// MARK: Properties
#Published var fetchedImages: [ImageAsset] = []
#Published var selectedImages: [ImageAsset] = []
init(){
fetchImages()
}
// MARK: Fetching Images
func fetchImages(){
let options = PHFetchOptions()
// MARK: Modify As Per Your Wish
options.includeHiddenAssets = false
options.includeAssetSourceTypes = [.typeUserLibrary]
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
PHAsset.fetchAssets(with: .image, options: options).enumerateObjects { asset, _, _ in
let imageAsset: ImageAsset = .init(asset: asset)
self.fetchedImages.append(imageAsset)
}
PHAsset.fetchAssets(with: .video, options: options).enumerateObjects { asset, _, _ in
let imageAsset: ImageAsset = .init(asset: asset)
self.fetchedImages.append(imageAsset)
}
}
}
On my view I have this:
.popupImagePicker(show: $showPicker) { assets in
// MARK: Do Your Operation With PHAsset
// I'm Simply Extracting Image
// .init() Means Exact Size of the Image
let manager = PHCachingImageManager.default()
let options = PHImageRequestOptions()
options.isSynchronous = true
DispatchQueue.global(qos: .userInteractive).async {
assets.forEach { asset in
manager.requestImage(for: asset, targetSize: .init(), contentMode: .default, options: options) { image, _ in
guard let image = image else {return}
DispatchQueue.main.async {
self.pickedImages.append(image)
}
}
}
}
}

Related

Load Photo Library iOS 14 With Limited Access

I have an app that takes pictures that are stored in the photos library. I would like to be able to load just the images taken with the app into an app library .I have two functions that load the photos library and then request an individual image. The images are requested in a foreach loop. That works fine with full access. However, with limited access I get nothing. If I use the photo picker I get the pictures selected.
My retrieval code is:
func loadLibrary() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
assets = PHAsset.fetchAssets(with: fetchOptions)
}
func loadImage(_ asset: PHAsset) -> UIImage? {
var image: UIImage? = nil
let option = PHImageRequestOptions()
option.isSynchronous = true
option.isNetworkAccessAllowed = true
option.resizeMode = .fast
manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: option) { img, err in
guard let img = img else { return }
image = img
}
return image
}
My saving code:
final class ImageSaver: NSObject, ObservableObject {
public static let shared = ImageSaver()
let objectDidChange = PassthroughSubject<Void, Never>()
#Published var saved = false {
didSet {
if saved {
objectDidChange.send()
}
}
}
func writeToPhotoAlbum(image: UIImage) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil)
}
#objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("Image not saved. Error: \(error)")
self.saved = false
} else {
print("Save finished!")
self.saved = true
}
}
}
In the WWDC2020 Session Video Handle the Limited Photos Library in your app the video states that "When your app creates new assets they will automatically be included as part of the user's selection for the application." This is exactly the behavior I want, but it is not the behavior I am getting. Changing the privacy settings shows the fetch and load are working as expected.

How to fetch all albums with images from iPhone without taking time in Swift?

I tried to fetch all images from album. It fetches all images with their URL's and image data, but it does not fetch images directly from given URL path, So I need to download images in Document Directory and then get path. So it's taking too much time. I use below code. I want fetch images like iPhone photos library fetches.
Please find error.
func fatchImagesfromAlbum() {
DispatchQueue.global(qos: .background).async {
self.photoAssets = self.fetchResult as! PHFetchResult<AnyObject>
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
self.photoAssets = PHAsset.fetchAssets(in: self.assetCollection, options: fetchOptions) as! PHFetchResult<AnyObject>
for i in 0..<self.photoAssets.count{
autoreleasepool {
let asset = self.photoAssets.object(at: i)
let imageSize = CGSize(width: asset.pixelWidth,
height: asset.pixelHeight)
let options = PHImageRequestOptions()
options.deliveryMode = .fastFormat
options.isSynchronous = true
options.isNetworkAccessAllowed = true
self.imageManager.requestImage(for: asset as! PHAsset, targetSize: imageSize, contentMode: .aspectFill, options: options, resultHandler: { (image, info) -> Void in
if image != nil {
let image1 = image as! UIImage
let imageUrl = info!["PHImageFileURLKey"] as? NSURL
let imageName = imageUrl?.lastPathComponent
let urlString: String = imageUrl!.path!
let theFileName = (urlString as NSString).lastPathComponent
self.imageName.append("\(theFileName)")
self.imagePath.append("\(urlString)")
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
DispatchQueue.global(qos: .background).async {
if !FileManager.default.fileExists(atPath: localPath!.path) {
do {
try UIImageJPEGRepresentation(image1, 0.1)?.write(to: localPath!)
print("file saved")
}catch {
print("error saving file")
}
}
else {
print("file already exists")
}
}
}
})
DispatchQueue.main.async
{
self.collectionView.reloadData()
}
}
}
self.hudHide()
}
PHPhotoLibrary.shared().register(self)
if fetchResult == nil {
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
fetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
}
}
I recommend simply using UIImagePickerController or if your app requires multiple image selection functionality, a third-party library like DKImagePickerController. As another user already mentioned in the comments, these will only copy the image(s) the user selected into your app's directory and save on processing time.

How to add pagination to PHAsset fetching from Photos Framework?

I am trying to get all the photos from cameraRoll using Photos framework but its taking a lot of time to fetch all the photos from cameraRoll.
Is their anyway to add pagination to it ?
so i can fetch while scrolling.
var images = [UIImage]()
var assets = [PHAsset]()
fileprivate func assetsFetchOptions() -> PHFetchOptions {
let fetchOptions = PHFetchOptions()
//fetchOptions.fetchLimit = 40 //uncomment to limit photo
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
fetchOptions.sortDescriptors = [sortDescriptor]
return fetchOptions
}
fileprivate func fetchPhotos() {
let allPhotos = PHAsset.fetchAssets(with: .image, options: assetsFetchOptions())
DispatchQueue.global(qos: .background).async {
allPhotos.enumerateObjects({ (asset, count, stop) in
//print(count)
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 200, height: 200)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
self.images.append(image)
self.assets.append(asset)
}
if count == allPhotos.count - 1 {
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
}
})
})
}
}
allPhotos is of type PHFetchResult< PHAsset > which is a lazy collection, ie it doesn't actually go out and get the photo until you ask it for one, which is what .enumerateObjects is doing. You can just grab the photos one at a time with the subscript operator or get a range of objects with objects(at:) to page through the collection as needed.

how to get image from PHImageFileURLKey

guys.
I am getting images URL from photos by using below code.
func getAllImagesURL() -> [URL]
{
var arr_URL = [URL]()
for index in 0..<fetchResult.count
{
imgManager.requestImageData(for: fetchResult.object(at: index) as PHAsset, options: requestOptions, resultHandler: { (imagedata, dataUTI, orientation, info) in
if let fileName = (info?["PHImageFileURLKey"] as? URL)
{
//do sth with file name
arr_URL.append(fileName)
}
})
}
return arr_URL
}
By using this URL key I want to get the image from photos.I have searched and found below code.But it still not working.
func getImage(assetUrl: URL) -> UIImage? {
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
guard let result = asset.firstObject else {
return nil
}
var assetImage: UIImage?
let options = PHImageRequestOptions()
options.isSynchronous = true
PHImageManager.default().requestImage(for: result, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: options) { image, info in
assetImage = image
}
return assetImage
}
It returns nil.So please help me.How to get the image by using URL key.
Thanks in Advance..
In the getImage(assetUrl: URL) -> UIImage? method, you are using
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
here assetUrl is the url that should be taken from if you are using the AssetsLibrary. This library is deprecated from iOS 9.0 onwards. We have to use Photos library instead.
BTW, you are already getting all the images(data) in getAllImageURLs() method. Just convert that method to get all the images and process those images as required. You can use the below method to get all the images.
func getAllImages() -> [UIImage]?
{
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
var allImages = [UIImage]()
for index in 0..<fetchResult.count
{
let asset = fetchResult.object(at: index) as PHAsset
imgManager.requestImage(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: .aspectFill, options: requestOptions, resultHandler: { (uiimage, info) in
allImages.append(uiimage!)
})
}
return allImages
}
NOTE: tweak this method as per your requirements.

Loading image from gallery by local URL (Swift)

I’ve encountered a strange behavior:
An imagePicker returns a PHAsset, and I do the Following and manage to present an imageView with image from the data:
asset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { (input, _) in
let url = input?.fullSizeImageURL
let imgV = UIImageView()
let test_url = URL(string: (url?.absoluteString)!)
print("><><><^^^><><>\(test_url)")
//prints: ><><><^^^><><>Optional(file:///var/mobile/Media/DCIM/107APPLE/IMG_7242.JPG)
let data = NSData(contentsOf: test_url! as URL)
imgV.image = UIImage(data: data! as Data)
imgV.backgroundColor = UIColor.cyan
att.imageLocalURL = url?.absoluteString//// saving the string to use in the other class
imgV.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
self.view.addSubview(imgV) /// just to test that the file exists and can produce an image
However when I do the following in another class:
if((NSURL( string: self.attachment.imageLocalURL! ) as URL!).isFileURL)// checking if is Local URL
{
let test_url = URL(string: self.attachment.imageLocalURL!) // reading the value stored from before
print("><><><^^^><><>\(test_url)")
//prints :><><><^^^><><>Optional(file:///var/mobile/Media/DCIM/107APPLE/IMG_7242.JPG)
let data = NSData(contentsOf: test_url! as URL)
imageView.image = UIImage(data: data! as Data)
}
The data is nil! What am I doing wrong, the String for URL is identical in both cases!
PHAsset objects should be accessed via the PHImageManager class. If you want to load the image synchronously I recommend you do something like this:
func getImage(assetUrl: URL) -> UIImage? {
let asset = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
guard let result = asset.firstObject else {
return nil
}
var assetImage: UIImage?
let options = PHImageRequestOptions()
options.isSynchronous = true
PHImageManager.default().requestImage(for: result, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: options) { image, info in
assetImage = image
}
return assetImage
}
You could even write a UIImageView extension to load the image directly from a PHAsset url.
var images:NSMutableArray = NSMutableArray() //hold the fetched images
func fetchPhotos ()
{
images = NSMutableArray()
//totalImageCountNeeded = 3
self.fetchPhotoAtIndexFromEnd(0)
}
func fetchPhotoAtIndexFromEnd(index:Int)
{
let status : PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
if status == PHAuthorizationStatus.Authorized
{
let imgManager = PHImageManager.defaultManager()
let requestOptions = PHImageRequestOptions()
requestOptions.synchronous = true
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
if let fetchResult: PHFetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
{
if fetchResult.count > 0
{
imgManager.requestImageForAsset(fetchResult.objectAtIndex(fetchResult.count - 1 - index) as! PHAsset, targetSize: CGSizeMake(self.img_CollectionL.frame.size.height/3, self.img_CollectionL.frame.size.width/3), contentMode: PHImageContentMode.AspectFill, options: requestOptions, resultHandler: { (image, _) in
//self.images.addObject(image!)
if image != nil
{
self.images.addObject(image!)
}
if index + 1 < fetchResult.count && self.images.count < 20 //self.totalImageCountNeeded
{
self.fetchPhotoAtIndexFromEnd(index + 1)
}
else
{
}
})
self.img_CollectionL.reloadData()
}
}
}
}

Resources