How to upload PHAssets in background - ios

In Objective-C I want to upload all PHAssets (images and videos) to my server in background. Can any please suggest me how can I do this?

let mPhasset : PHAsset = PHAsset()
if mPhasset.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
mPhasset.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable: Any]) -> Void in
if let url = contentEditingInput?.fullSizeImageURL {
// IMPORTANT
// this url avaiable only in this block
// we can't perform async operation in bg
// cause access will expired
// Now you can copy using FileManager
// and after this upload copied file
}
})
// Alternative
let imageManager = PHCachingImageManager()
let opt = PHImageRequestOptions()
opt.resizeMode = .exact
opt.deliveryMode = .highQualityFormat
imageManager.requestImage(for: mPhasset,
targetSize: CGSize(width: mPhasset.pixelWidth, height: mPhasset.pixelHeight),
contentMode: .aspectFill,
options: opt,
resultHandler:
{ (image: UIImage?, _) in
// now you can go to background and do whatever you want
// benefit that you don't need to copy file in main thread
// which can lag your app
// disadvantage of this method: we load image in memory
})
} else if mPhasset.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .current
PHImageManager.default().requestExportSession(forVideo: mPhasset, options: options, exportPreset: AVAssetExportPresetHighestQuality, resultHandler: { (session, info) in
// if we just request video url asset and try upload to server
// we will have problems with slow mo videos
// also we will be on the main thread and access for video will expire, if we go to background
// + size of video is very huge
guard let session = session else { return }
session.outputURL = URL(fileURLWithPath: "our output path")
session.outputFileType = AVFileTypeQuickTimeMovie
session.shouldOptimizeForNetworkUse = true
session.exportAsynchronously {
if (session.status == .completed) {
// success
} else {
}
}
})
}

Related

iOS Photos library - how to get the REAL filename?

I am looking for either a Swift or Objective C solution for this. Here, I am showing my example using swift.
I am using the Photos framework on iOS and having a hard time finding the REAL filename instead of the IMG_4227.JPG type. The name of my image is actually myawesomefilename.jpg and I see this file name if I airdrop or transfer to dropbox the image from my iPhone. But when I use the Photos framework, I get the names as IMG_4227.JPG.
There is an app on the app store which is able to get the real file names of all images, so it is definitely possible. I just don't know how.
Here's how I am currently getting the IMG_4227.JPG type names:
func exifTapped(asset: PHAsset) {
print("name: \(asset.value(forKey: "filename"))")
getURL(ofPhotoWith: asset) { (url) in
print("URL: \(url)")
let data = NSData.init(contentsOf: url!)!
if let imageSource = CGImageSourceCreateWithData(data, nil) {
let imageProperties2 = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)! as NSDictionary
print("imageProperties2: ", imageProperties2)
}
}
}
func getURL(ofPhotoWith mPhasset: PHAsset, completionHandler : #escaping ((_ responseURL : URL?) -> Void)) {
if mPhasset.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.isNetworkAccessAllowed = true
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
mPhasset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
completionHandler(contentEditingInput!.fullSizeImageURL)
})
} else if mPhasset.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
options.isNetworkAccessAllowed = true
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)
}
})
}
}
EDIT: None of the duplicate solutions are any different than what I already have and they all give the IMG_4227.JPG type names instead of the real name. So this is not a duplicate.
I figured it out.
let resources = PHAssetResource.assetResources(for: asset)
print("Filename: \((resources.first as! PHAssetResource).originalFilename)")

App crashed when i select a photo from iCloud

I am trying to fetch all photos from my device, and show them in a collection:
var allPhotos : PHFetchResult<PHAsset>? = nil
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
let fetchOptions = PHFetchOptions()
self.allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
print("Found \(allPhotos.count) assets")
case .denied, .restricted:
print("Not allowed")
case .notDetermined:
// Should not see this when requesting
print("Not determined yet")
}
}
This code fetches all the photos from the device and as well as from iCloud and then stores them into allPhotos array. But when I am trying to set them using this function in collection view cell, I am only getting photos from the device. The other photos cell is empty. Converting images with this function:
func getUIImage(asset: PHAsset) -> UIImage? {
var img: UIImage?
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
return img
}
And for some images I am getting this error "Failed to load image data for asset " for those empty cells. Can someone explain why it is not fetching iCloud images?
Because the following code is a closure.
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
And closures execute on background thread and are not inline. Closure execution may take time and might not return data instantaneously.
What you could try is creating a closure callback for image to load.
func getUIImage(asset: PHAsset, complition : #escaping ((_ img:UIImage?,_ error:String?) -> Void)){
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data,
let img = UIImage(data: data){
complition(img,nil)
}else{
complition(nil,"Something went wrong")
}
}
}
You can try this. Hope this help.
Try to add it in requestOptions:
options.isNetworkAccessAllowed = true
It helped me

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()
}
}
}
}

iOS : How to convert a video PHAsset to NSData

I want to put the videos stored on my iPhone to my Google Drive.
I have already done with images, but with videos, it's an other problem...
For images, no problem, I convert my asset to an NSData with this method :
data = UIImagePNGRepresentation(result!)!
And I put the image to my drive !
But, for videos, I tried many different ways, but no, I can't.
How can I do ?
Thanks a lot !
I did it !
This is the solution :
PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) in
dispatch_async(dispatch_get_main_queue(), {
let asset = asset as? AVURLAsset
var data = NSData(contentsOfURL: asset.URL)
})
})
And after, you have the good NSData variable which you can use to put your video to the Cloud !
Please add Bellow solution its work for me
if you miss option.isNetworkAccessAllowed = true then you get error for genera the url
private let options: PHVideoRequestOptions = PHVideoRequestOptions()
option.isNetworkAccessAllowed = true
PHImageManager.default().requestAVAsset(-------
Updated for Swift 5
PHImageManager or PHCachingImageManager can be used here
PHImageManager.default().requestAVAsset(forVideo: asset,
options: nil) { (asset, audioMix, info) in
if
let asset = asset as? AVURLAsset,
let data = NSData(contentsOf: asset.url) {
//do smth with data
}
}
}
Fetch synchronously Image/Video Swift 5 + caching
extension PHAsset {
func getImage() -> UIImage? {
let manager = PHCachingImageManager.default
let option = PHImageRequestOptions()
option.isSynchronous = true
var img: UIImage? = nil
manager().requestImage(for: self, targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight), contentMode: .aspectFit, options: nil, resultHandler: {(result, info) -> Void in
img = result!
})
return img
}
func getVideo() -> NSData? {
let manager = PHCachingImageManager.default
let option = PHImageRequestOptions()
option.isSynchronous = true
var resultData: NSData? = nil
manager().requestAVAsset(forVideo: self, options: nil) { (asset, audioMix, info) in
if let asset = asset as? AVURLAsset, let data = NSData(contentsOf: asset.url) {
resultData = data
}
}
return resultData
}
}

Resources