Can't save video to Parse in swift - swift

I am simply trying to have the user take a short video in the application, and when the video is recorded, the app would send it to the Parse back-end once the user clicks 'Use Video' using UIImagePickerController.
I am able to save the video locally in the idFinishPickingMediaWithInfo with
UISaveVideoAtPathToSavedPhotosAlbum(pathString!, self, nil, nil)
But I can't find anything online to save the video to Parse.
The line that that is running the error is
let videoFile:PFFile = PFFile(data: videoData)
With error handler - Cannot convert value of type 'String?' to expected argument type 'NSData'
I believe it has something to do with this line above it causing the error:
let videoData = tempImage.relativePath
I can't find code to get it working. Any help would me amazing!
#IBAction func recordAction(sender: AnyObject) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) {
print("Camera Avilable")
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.videoMaximumDuration = 180 // Perhaps reduce 180 to 120
imagePicker.videoQuality = UIImagePickerControllerQualityType.TypeMedium
imagePicker.allowsEditing = false
imagePicker.showsCameraControls = true
self.presentViewController(imagePicker, animated: true, completion: nil)
} else {
print("Camera Unavailable")
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let tempImage = info[UIImagePickerControllerMediaURL] as! NSURL!
let pathString = tempImage.relativePath
self.dismissViewControllerAnimated(true, completion: nil)
let videoData = tempImage.relativePath
let videoFile:PFFile = PFFile(data: videoData)
PFUser.currentUser()?.setObject(videoFile, forKey: "videoFile")
PFUser.currentUser()?.saveInBackground()
}

let videoData = NSData(contentsOfFile:tempImage.relativePath)
let videoFile:PFFile = PFFile(name:"yourfilenamewithtype", data:videoData)
file.saveInBackgroundWithBlock({(succeeded:Bool,error:NSError?)
//Handle success or failure here.
},progressBlock:{(percentDone: Int32) -> Void in
//Update your progress spinner here.percentDone will be between 0 and 100.
})
yourfilenamewithtype like this,resume.txt
The method is the try to make file to NSdata.

I have no experience with Parse, but the documentation suggests that the PFFile(data:) constructor indeed expects the contents of the file, and you give the name. You likely want to use PFFile(name: nil, contentsAtPath: pathString!).
Note that you set pathString and videoData to the same value, which is the path and not the data.
More importantly, note that if you simply load the file with NSData(contentsOfFile: pathString!) as RYANCOAL9999 suggested, you are putting the entire video file into RAM. You could use NSData(contentsOfFile: pathString!, options: .DataReadingMappedIfSafe), but I believe it's best to simply delegate the entire file copying process to the libraries.

Related

didFinishPickingMediaWithInfo function goes on infinite loop

It looks like the didFinishPickingMediaWithInfo function is going on an infinite loop and it eventually crashes with an error that says in the console:
warning: could not execute support code to read Objective-C class data in >the process. This may reduce the quality of type information available.
Right when I record a video and press the choose button, it crashes because it calls the didFinishPickingMediaWithInfo. Here is the relevant code:
let imagePicker: UIImagePickerController! = UIImagePickerController()
let saveFileName = "/test.mp4"
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {
if UIImagePickerController.availableCaptureModes(for: .rear) != nil {
//if the camera is available, and if the rear camera is available, the let the image picker do this
imagePicker.sourceType = .camera
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.allowsEditing = false
imagePicker.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
imagePicker.videoMaximumDuration = 60
imagePicker.videoQuality = .typeIFrame1280x720
present(imagePicker, animated: true, completion: nil)
} else {
postAlert("Rear camera doesn't exist", message: "Application cannot access the camera.")
}
} else {
postAlert("Camera inaccessable", message: "Application cannot access the camera.")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
let videoURL = info[UIImagePickerControllerReferenceURL] as? NSURL
print("\(String(describing: videoURL))" )
guard let path = videoURL?.path else { return }
let videoName = path.lastPathComponent
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = paths.first as String!
let localPath = documentDirectory! + "/" + videoName
guard let imageData = NSData(contentsOfFile: localPath) else { return }
let image = UIImage(data: imageData as Data)
picker.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker.delegate = self
}
Thank you in advance!
You are calling the function from inside of itself, here:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
That is causing your infinite loop.

xcode 8.1 & iOS 10.1.1 "[Generic] Creating an image format with an unknown type is an error" for UIImagePickerController

Trying to allow a user to select a photo from their photo library and then display that image in a UIImageView. Photo library is showing up just fine, but when I select a photo from my library, I get this error "[Generic] Creating an image format with an unknown type is an error".
Stepped through my code below but the error comes up only when selecting an image which does not occur inside either of these 2 functions.
#IBAction func openPhotoLibraryButton(sender: UIButton) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary;
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage!
let imageData = UIImageJPEGRepresentation(image!, 0.6)
let compressedJPGImage = UIImage(data: imageData!)
imagePicked.image = compressedJPGImage
}
Tried the solutions suggested here: xCode 8 - Creating an image format with an unknown type is an error but none of them worked, and it sounds like several folks did not get this issue resolved. Any ideas?
did you try adding _ before picker? I believe the method was changed and the one you are using is deprecated, you should be able to let it autocomplete.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
if let imageData = UIImageJPEGRepresentation(image, 0.6) {
let compressedJPGImage = UIImage(data: imageData)
imagePicked.image = compressedJPGImage
}
} else {
print("Image Picker Failure")
//handle any possible image picker issues/failures so that app doesn't crash in the future
//troubleshoot further if it always hits this
}
dismissViewControllerAnimated(true, completion: nil)
}

Save image in Realm

I'm trying to pick image from device's Photo Library in method:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
userPhoto.image = info[UIImagePickerControllerOriginalImage] as! UIImage?
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
dismiss(animated: true, completion: nil)
}
and save this picture in Realm (as NSData):
asset.assetImage = UIImagePNGRepresentation(userPhoto.image!)! as NSData?
...
try! myRealm.write
{
user.assetsList.append(asset)
myRealm.add(user)
}
After build succeeded and trying to pick and save image (in the app) i have app error:
'Binary too big'
What i'm doing wrong?
P.S. Sorry for my English :)
After some actions i have this code. But it's overwrite my image.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
let image = info[UIImagePickerControllerOriginalImage]as! UIImage
let data = UIImagePNGRepresentation(image)
do
{
try data?.write(to: localPath!, options: Data.WritingOptions.atomic)
}
catch
{
// Catch exception here and act accordingly
}
userPhoto.image = image
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
urlCatch = (localPath?.path)!
self.dismiss(animated: true, completion: nil);
}
Don't save the image itself into realm, just save the location of the image into realm as String or NSString and load the image from that saved path. Performance wise it's always better to load images from that physical location and your database doesn't get too big
func loadImageFromPath(_ path: NSString) -> UIImage? {
let image = UIImage(contentsOfFile: path as String)
if image == nil {
return UIImage()
} else{
return image
}
}
or you just save the image name, if it's in your documents directory anyhow
func loadImageFromName(_ imgName: String) -> UIImage? {
guard imgName.characters.count > 0 else {
print("ERROR: No image name")
return UIImage()
}
let imgPath = Utils.getDocumentsDirectory().appendingPathComponent(imgName)
let image = ImageUtils.loadImageFromPath(imgPath as NSString)
return image
}
and here a rough example how to save a captured image to your directory with a unique name:
#IBAction func capture(_ sender: AnyObject) {
let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (imageDataSampleBuffer, error) -> Void in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
//self.stillImage = UIImage(data: imageData!)
//self.savedImage.image = self.stillImage
let timestampFilename = String(Int(Date().timeIntervalSince1970)) + "someName.png"
let filenamePath = URL(fileReferenceLiteralResourceName: getDocumentsDirectory().appendingPathComponent(timestampFilename))
let imgData = try! imageData?.write(to: filenamePath, options: [])
})
/* helper get Document Directory */
class func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
//print("Path: \(documentsDirectory)")
return documentsDirectory as NSString
}
https://realm.io/docs/objc/latest/#current-limitations
maximum data size is 16 MB . this is limitation of realm
Depending on how your serializing the image data (for example if it's a lossless bitmap), it's quite possible that this data exceed 16MB, which as you've stated is Realm's maximum supported size for binary properties.
When you invoke NSData.length, how large does it say your data is?

Document file path unreachable in swift

I'm currently working on a small swift application and I'm storing some video records in the documents folder of the app. I would like to retrieve these on a later moment. I already got an array of file locations like this:
file:///private/var/mobile/Containers/Data/Application/6C462C4E-05E2-436F-B2E6-F6D9AAAC9361/Documents/videorecords/196F9A75-28C4-4B65-A06B-6111AEF85F01.mov
Now I want to use such file location to create a thumbnail with the first frame and connect that to my imageview with the following piece of code:
func createVideoStills() {
for video in directoryContents {
print("\(video)")
do {
let asset = AVURLAsset(URL: NSURL(fileURLWithPath: "\(video)"), options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(0, 1), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
videoCell.imageView = UIImageView(image: uiImage)
//let imageView = UIImageView(image: uiImage)
} catch let error as NSError {
print("Error generating thumbnail: \(error)")
}
}
}
The first print gives me a path like described above. But the AVURLAsset doesn't like this path because it spits out the following error:
Error generating thumbnail: Error Domain=NSURLErrorDomain Code=-1100
"The requested URL was not found on this server."
UserInfo={NSLocalizedDescription=The requested URL was not found on
this server., NSUnderlyingError=0x14ee29170 {Error
Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Which is weird cause it is right there. Any solutions on how to fix/solve this?
Kind regards,
Wouter
The output of your print("\(video)") is not a file path but a string representation of file URL. You need to use init(string:) than init(fileURLWithPath:) of NSURL.
See what you get with:
let asset = AVURLAsset(URL: NSURL(string: video), options: nil)
(Unnecessary string interpolation would generate some unexpected result without errors -- like getting "Optional(...)", so you should avoid.)
// if You Are Using PHPickerViewController then do this for fetching the url.
// ------
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let provider = results.first?.itemProvider else { return }
if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
provider.loadItem(forTypeIdentifier: UTType.movie.identifier, options: [:]) { [self] (videoURL, error) in
print("resullt:", videoURL, error)
DispatchQueue.main.async {
if let url = videoURL as? URL {
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
present(playerVC, animated: true, completion: nil)
}
}
}
}
}

Video is not saving in parse

The output says:
- Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.
- Save successful
But when I go into the parse backend nothing is saved.
#IBAction func recordAction(sender: AnyObject) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
print("Camera Available")
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.videoMaximumDuration = 180 // Perhaps reduce 180 to 120
imagePicker.videoQuality = UIImagePickerControllerQualityType.TypeMedium
imagePicker.allowsEditing = false
imagePicker.showsCameraControls = true
self.presentViewController(imagePicker, animated: true, completion: nil)
}
else {
print("Camera Unavailable")
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let Video = PFObject(className:"Video")
Video["user"] = PFUser.currentUser()
let tempImage = info[UIImagePickerControllerMediaURL] as! NSURL!
_ = tempImage.relativePath
let videoData = NSData(contentsOfFile:tempImage.relativePath!)
let videoFile:PFFile = PFFile(name:"consent.mp4", data:videoData!)!
Video["videoFile"] = videoFile
self.dismissViewControllerAnimated(true, completion: nil)
videoFile.saveInBackgroundWithBlock({ (succeeded: Bool, error: NSError?) -> Void in
// Handle success or failure here ...
if succeeded {
print("Save successful")
} else {
print("Save unsuccessful: \(error?.userInfo)")
}
}, progressBlock: { (amountDone: Int32) -> Void in
})
}
I figured it out. I had an extra column in the database that was unaccounted for in the apps code. When i removed it, the data was sent successfully.