iOS Swift 2 - Get path to video in camera roll - ios

I'm working on an app that uploads a video on a server and I got to a point where I need to re-run the upload process if a user shuts down the app and openes it again later.
After a user taps on a video, this function gets triggered
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType == kUTTypeMovie {
let fileUrl = info[UIImagePickerControllerMediaURL] as! NSURL
// ...
}
self.performSegueWithIdentifier("someSegue", sender: self)
}
But I found out that fileUrl points to some temporary reference to the original video and is always different even if the video is the same.
So how can I get real path to the video, which I can save to NSUserDefaults so when a user opens the app again I can access the video again.

The UIImagePicker will always compress a video and hands you the NSURL of the temp file given in info[UIImagePickerControllerMediaURL]--and it is your responsibility to delete that temp file whether you use it or not.
If you want to use the compressed version, move it to your documents folder and save the path to that in your NSUserDefaults.
If you want the original raw video, get the NSURL in info[UIImagePickerControllerReferenceURL]. This is the URL to the original ALAsset. Then you'll need to use PHImageManager().requestImageDataForAsset or PHImageManager().requestExportSessionForVideo to get the data for upload.

Related

Getting metadata of image from photoLibrary

I need to get the metaData from an Image I'm picking via UIImagePickerController.
This is my code:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let metaData = info[UIImagePickerController.InfoKey.mediaMetadata] as? [AnyHashable: Any]
print(metaData)
picker.dismiss(animated: true, completion: nil)
}
It works fine, when im picking the Image having .camera as source. But when I use .photoLibrary as source, then metaData is nil. I already read through other questions and tried stuff like
let asset = info[.phAsset] as? PHAsset
print(asset?.creationDate ?? "None")
print(asset?.location ?? "None")
But this also returns nil. I guess the source of the problem is, that the info-Dictionary only returns 4 Keys when picking from .photoLibrary:
UIImagePickerControllerOriginalImage
UIImagePickerControllerMediaType
UIImagePickerControllerImageURL
UIImagePickerControllerReferenceURL
It would be awesome if someone could tell me where my mistake is.
Thanks in advance !
I had the same problem. If the user does not grant access to the photo library first, info[.phAsset] will return nil.
Quote from Apple: "Accessing the photo library always requires explicit permission from the user. The first time your app uses PHAsset, PHCollection, PHAssetCollection, or PHCollectionList methods to fetch content from the library ..."
Thus, you have to call PHPhotoLibrary.requestAuthorization{ ... } before presenting the image picker.
If the user denies the request, info[.phAsset] will also be nil!
This key is valid only when using an image picker whose source type is set to UIImagePickerController.SourceType.camera, and applies only to still images.
The value for this key is an NSDictionary object that contains the metadata of the photo that was just captured. To store the metadata along with the image in the Camera Roll, use the PHAssetChangeRequest class from the Photos framework.
https://developer.apple.com/documentation/uikit/uiimagepickercontroller/infokey/1619147-mediametadata

iOS Swift - Not able to use video from Library

I'm trying to obtain a video from the library in my swift app.
I'm using the following code snippet :
print("ChatDetailsController - imgaction - Vid from Lib pressed")
self.selection = "videoLib";
self.imgSelection = "vidFromLib"
self.imagePicker.sourceType = .PhotoLibrary
self.imagePicker.delegate = self
self.imagePicker.mediaTypes = ["public.movie"]
self.presentViewController(self.imagePicker!, animated: true, completion: nil)
Then in delegate method:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
if(selection == "videoLib")
{
if let videoURL = info[UIImagePickerControllerReferenceURL] as? NSURL
{
print("imagePickerController - reached for video - from Lib - \(videoURL)")
let videoData = NSData(contentsOfURL: videoURL)
}
}
}
When running the app, it shows the library and I can select a video, then it says compressing video.. but then the app crashes.
The Crash happens when I try to access the videoData Variable becuase it wasn't able to get the actual data since the path is an asset path and not a video path.
The Log shows something similar to this:
fatal error: unexpectedly found nil while unwrapping an Optional value
The print function shows:
imagePickerController - reached for video - from Lib - assets-library://asset/asset.MOV?id=7D3BFA00-A7CB-470C-8899-F82FCC2CBC54&ext=MOV
Also, right after choosing/selecting the video, a message similar to this is displayed in the console/log:
[Generic] video path /var/mobile/Media/PhotoData/CPLAssets/group419/A2901DD1-FDEF-423B-B4A3-E808B9AC45E1.MOV for PLPreviewImageAndDurationForVideoAtPath does not exist
Of course the file name changes on every run.
I'm able to get a video and images from the camera, and getting images from the library, I'm only having trouble with using the the video from the library. I would expect to see the video URL similar to :
/var/mobile/Containers/Data/Application/BEDDC883-B20E-496D-8C9A-A40EC0A29214/Documents/63ea24932c101b76c72fcbae3993fc9b.mov for example; where the video file is in the application and not showing as an unusable asset URL.
Any help would be appreciated.
The problem is that you're trying to fetch the wrong video. You're trying to use info[UIImagePickerControllerReferenceURL]. What you want is info[UIImagePickerControllerMediaURL].

Load image from the file after rewrite it - UIImage

I have silly problem with loading image from the file. I have two views putted to UITabBarController.
At the first view user can load his image from the Photo Library or Camera. Second view present this photo. This file is on the server. If user doesn't choose his image server sent custom image. If there is uploaded photo it will push user's picture.
When user tap button there is a menu with options. For example we will decide to take picture from the Photo Library. After user took image:
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
self.saveUserImage(userID, imageData: UIImagePNGRepresentation(image)!)
apiManager.userUploadProfile(userID, imageData: UIImagePNGRepresentation(image)!)
userImageView.image = image
}
func saveUserImage(userUUID: String, imageData: NSData) {
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last
let savePath = path! + "/\(userUUID)-user.png"
NSFileManager.defaultManager().createFileAtPath(savePath, contents: imageData, attributes: nil)
}
After that point user can see chosen picture and everything is okey. When user change tab, second view will refresh all data on it and again will download all images and data from the server. Unfortunately images that contains old user image doesn't refresh and there is still old photo.
When we come back to the first tab image is going back to old image but after few seconds.
The strangest thing is if I am checking server there is new uploaded image and in the app container it exist too. When I restart the app everything works perfectly.
It looks like this image is saved in the memory and iOS takes old version from RAM or from some other swap. How refresh UIImageView and show current saved image?
EDIT:
There is a method which load the image
func userProfilePicture(userId: String) -> UIImage {
let cacheDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last
let savePath = cacheDirectory! + "/\(userId)-user.png"
if NSFileManager.defaultManager().fileExistsAtPath(savePath) {
if let image = UIImage(named: savePath) {
return image
}
}
return UIImage(named: "test_avatar")!
}
I can't comment but i was facing a similar issue but i have a question, does the picture get uploaded before the user changes tabs, or after? Even if you call the function to update the database, it takes some time to actually "send the new photo/path to the photo to the database". Here are your options, if the picture is not in the server at the time of the switching tabs, implement a loading ui until it has successfully uploaded and the new, updated value, into the database. An easy way to do that would be to use the SVProgressHUD pod and set the default mask type to .clear which disables user interaction. Option 2, is to pass the actual UIImage via a static variable, or in the prepare for segue method, or through a struct, or any other way and set the picture that needs to be refreshed to the uiimage without getting it from the server only when you switch tabs.
Okey, I found the answer. The problem was in initialize UIImage. We have two methods to load image from file.
First one load the image and cache it in memory. Later it use only a reference to this image data in memory:
let image = UIImage(named: savePath)
Second method load image strictly from file every time when user use that function:
let image = UIImage(contentsOfFile: savePath)
Right now it works perfectly :D

How to convert a video NSURL to an ALAsset?

Facebook sharing requires an ALAsset such as the following:
let content = FBSDKShareVideoContent()
//The videos must be less than 12MB in size.
let bundle = NSBundle.mainBundle()
let path = bundle.URLForResource("a", withExtension: "mp4")
let video = FBSDKShareVideo()
// doesn't work; needs to be an "asset url" (ALAsset)
//video.videoURL = path
content.video = video
let dialog = FBSDKShareDialog()
dialog.shareContent = content
dialog.show()
How is it possible to take a local bundle document, or an NSData object, and convert it to an ALAsset?
(My initial thinking was saving the video to the local camera roll, and then loading the list and selecting it, but that is unnecessary interface steps)
The documentation for an ALAsset states that
An ALAsset object represents a photo or a video managed by the Photo application.
so I'm pretty sure that you have to write the video to the camera roll before using it as an ALAsset. However, you don't need to open the camera roll and have a user pick the asset in order to use it. When writing to the ALAssetLibrary using
library.writeVideoAtPathToSavedPhotosAlbum(movieURL, completionBlock: { (newURL, error) -> Void
you get the asset url in that newUrl completion block variable. Use it in the Facebook sharing call
let content = FBSDKShareVideoContent()
content.video = FBSDKShareVideo(videoURL: newURL)
FBSDKShareAPI.shareWithContent(content, delegate: self)
NSLog("Facebook content shared \(content.video.videoURL)")
You can do this sharing inside the completion block if you so desire, or you can save the newUrl from the completion block and use it somewhere else.

UIImagePickerControllerReferenceURL returns nil, crashing the app

I'm trying to print all the image metadata in the imagePickerController: didFinishPickingMediaWithInfo function. When I use the info.objectForKey(UIImagePickerControllerReferenceURL) method, it returns nil and if try to use this result my app crashes. Does anyone know why it returns nil and what else can I use to print all the image metadata when I pick an image? (using UIImageJPEGRepresentation is not an option because the EXIF data is removed).
This is my code:
func imagePickerController(picker: UIImagePickerController!, didFinishPickingMediaWithInfo info: NSDictionary!)
{
let image = info.objectForKey(UIImagePickerControllerOriginalImage) as UIImage
let refURL : NSURL = info.objectForKey(UIImagePickerControllerReferenceURL) as NSURL
var localSourceRef: CGImageSourceRef = CGImageSourceCreateWithURL(refURL, nil)
var localMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(localSourceRef, 0, nil)
println("\n Photo data: \n")
println(localMetadata)
}
So it sounds like there are actually two questions here:
1) Why is UIImagePickerControllerReferenceURL returning a nil reference?
2) How can you get location data from the photo?
So, the answer to (1) is usually because you receive the callback didFinishPickingMedia before the OS as written the file to the image library.
The answer to #2 is much trickier, as showcased by this question's line of answers:
Reading the GPS data from the image returned by the camera in iOS iphone
There are a number of variables you need to account for:
iOS will strip the GPS data out if you haven't requested access to location data, so you'll need to prompt for location access using CLLocationManager.
If the user has geotagging disabled, you'll never get GPS coords.
If the phone can't get a GPS lock, iOS won't record the GPS coords.
Per this answer: https://stackoverflow.com/a/10338012/490180 you should be able to retrieve the raw UIImage and then create the CGImageSourceRef from the data property off of UIImage's CGImage. This effectively removes the need for you to ever access the ReferenceURL.

Resources