Swift: save video from NSURL to user camera roll - ios

I have a variable videoURL of type NSURL.
If I call println(videoURL) it would return something like this:
http://files.parsetfss.com/d540f71f-video.mp4
I have a button set up that should take this videoURL and save the video to the user's camera roll.
The best I have done is this:
UISaveVideoAtPathToSavedPhotosAlbum(videoPath: String!, completionTarget: AnyObject!, completionSelector: Selector, contextInfo: UnsafeMutablePointer<Void>)
While I'm not even sure if this will work or not, I can't figure out how to convert videoFile:NSURL into a videoPath.
Any help is appreciated on this.
Edit:
The following is unsuccessful:
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.relativePath, self, nil, nil)

AssetsLibrary is deprecated
1: import Photos
import Photos
2: Use this code to save video from url to camera library.
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(nsUrlToYourVideo)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
Swift 3 & Swift 4
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: urlToYourVideo)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}

The accepted answer no longer works with Swift 3.0 & iOS 10.
First, you need to set the following permission in your app's plist file:
Privacy - Photo Library Usage Description
Provide a string that is presented to the user explaining why you are requesting the permission.
Next, import photos:
import Photos
Finally, here is the updated code for Swift 3.0:
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileURL)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}

To save video from NSURL to user camera roll
func video(videoPath: NSString, didFinishSavingWithError error: NSError?, contextInfo info: AnyObject)
{
if let _ = error {
print("Error,Video failed to save")
}else{
print("Successfully,Video was saved")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let conversationField = self.conversation {
if (mediaType?.isEqual((kUTTypeMovie as NSString) as String))!
{
let theVideoURL: URL? = (info[UIImagePickerControllerMediaURL] as? URL)
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum((theVideoURL?.path)!))
{
UISaveVideoAtPathToSavedPhotosAlbum((theVideoURL?.path)!, self, #selector(ConversationDetailsViewController.video(videoPath:didFinishSavingWithError:contextInfo:)), nil)
}
}
self.dismiss(animated: true, completion: nil)
}
Reference from:: https://www.raywenderlich.com/94404/play-record-merge-videos-ios-swift

Try this instead for saving video in photo library in swift 4.2 and above
func requestAuthorization(completion: #escaping ()->Void) {
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
PHPhotoLibrary.requestAuthorization { (status) in
DispatchQueue.main.async {
completion()
}
}
} else if PHPhotoLibrary.authorizationStatus() == .authorized{
completion()
}
}
func saveVideoToAlbum(_ outputURL: URL, _ completion: ((Error?) -> Void)?) {
requestAuthorization {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetCreationRequest.forAsset()
request.addResource(with: .video, fileURL: outputURL, options: nil)
}) { (result, error) in
DispatchQueue.main.async {
if let error = error {
print(error.localizedDescription)
} else {
print("Saved successfully")
}
completion?(error)
}
}
}
}
Use of function
self.saveVideoToAlbum(/* pass your final url to save */) { (error) in
//Do what you want
}
Don't forgot to import Photos and add Privacy - Photo Library Usage Description to your info.plist

deprecated as of iOS 9
1: import AssetsLibrary
import AssetsLibrary
2: Use this code to save video from url to camera library.
ALAssetsLibrary().writeVideoAtPathToSavedPhotosAlbum(outputFileURL, completionBlock: nil)

Just use it and paste your video's url:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
let createAssetRequest: PHAssetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(string: /* your url */)!)!
createAssetRequest.placeholderForCreatedAsset
}) { (success, error) -> Void in
if success {
//popup alert success
}
else {
//popup alert unsuccess
}
}

Related

Saving Video using UIImagePickerController for a Dashcam App

I am trying to have an alert pop up to save video from a trigger from CoreMotion Data.
I am having trouble saving the video from the yes prompt.
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
imagePicker.sourceType = .camera;
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.allowsEditing = false
imagePicker.showsCameraControls = false
imagePicker.perform(#selector(UIImagePickerController.startVideoCapture), with: nil, afterDelay: 1)
// shows camera onto screen
self.present(imagePicker, animated: true)
do {
motion.accelerometerUpdateInterval = 0.25
motion.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
print(data as Any)
if let trueData = data {
//self.view.reloadInputViews()
let x = trueData.acceleration.x
let y = trueData.acceleration.y
let z = trueData.acceleration.z
let totalAcceleration = calculateMagnitude (no1:Float (x), no2: Float (y),no3: Float (z))
if (Float(totalAcceleration) > 2.00){
self.dismiss(animated: true, completion : nil)
let alert = UIAlertController (title: "Sudden acceleration detected", message: "Are you in an accident?", preferredStyle: .alert)
self.present(alert, animated: true, completion: nil)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: {(action:UIAlertAction!) in
print("User has selected Yes")//Here is where I want to save the video
}))
alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.default, handler: {(action:UIAlertAction!) in
print("User has selected No")
imagePicker.perform(#selector(UIImagePickerController.startVideoCapture), with: nil, afterDelay: 1)
// shows camera onto screen
self.present(imagePicker, animated: true)
}))
}
All the answers I have found haven't really helped me at all :[
TLDR: Run App. Phone records whats in front of it. Change in CoreMotion data. Yes or No Prompt pops up. No continues the recording. Yes saves the video.
Don't forget to tap on screen to start capture
import UIKit
import Photos
import MobileCoreServices
import CoreMotion
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
lazy var imagePicker: UIImagePickerController = {
let ip = UIImagePickerController()
ip.delegate = self
ip.sourceType = .camera
ip.mediaTypes = [kUTTypeMovie as String]
ip.allowsEditing = false
ip.showsCameraControls = false
return ip
}()
let motion: CMMotionManager = {
let motion = CMMotionManager()
motion.accelerometerUpdateInterval = 0.25
return motion
}()
var videoUrl: URL?
override func viewDidLoad() {
super.viewDidLoad()
// First Check for photo library permission
checkPhotoLibraryPermission()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startCapture()
}
func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
//handle authorized status
break
case .denied, .restricted :
//handle denied status
break
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
// as above
break
case .denied, .restricted:
// as above
break
case .notDetermined:
// won't happen but still
break
}
}
}
}
func startCapture() {
self.present(imagePicker, animated: true) {
self.imagePicker.perform(#selector(self.imagePicker.startVideoCapture), with: nil, afterDelay: 1)
}
motion.startAccelerometerUpdates(to: .main) { (data, error) in
if let error = error {
print(error)
return
}
guard let data = data else { return }
let x = data.acceleration.x
let y = data.acceleration.y
let z = data.acceleration.z
let totalAcceleration = sqrt(x*x + y*y + z*z)
if totalAcceleration > 2 {
self.imagePicker.stopVideoCapture()
self.imagePicker.dismiss(animated: true, completion: nil)
self.showAlert()
}
}
}
func showAlert() {
let alert = UIAlertController (title: "Sudden acceleration detected", message: "Are you in an accident?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: {(action:UIAlertAction!) in
print("User has selected Yes") // Here is where I want to save the video
if let videoUrl = self.videoUrl {
self.saveVideoToPhotos(videoUrl)
}
}))
alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.default, handler: {(action:UIAlertAction!) in
print("User has selected No")
self.present(self.imagePicker, animated: true) {
self.imagePicker.perform(#selector(self.imagePicker.startVideoCapture), with: nil, afterDelay: 1)
}
}))
self.present(alert, animated: true, completion: nil)
}
func saveVideoToPhotos(_ videoUrl: URL) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoUrl)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let videoUrl = info[.mediaURL] as? URL {
self.videoUrl = videoUrl
}
}
}

Recording video from ARKit

I would like to add a feature for users to record their ARKit experience. I'm taking the capturedImage of the ARFrame supplied by session(_ session: ARSession, didUpdate frame: ARFrame) and concatenating them into a video.
Unfortunately, ARFrame.capturedImage shows the frame of video captured by the camera, but doesn't include nodes placed by ARKit.
Is there any way to capture video coming from an ARSCNView?
I've tried this library, but it has major bugs (no shadows, large stutter at beginning of recording). I'd also like to not use ReplayKit for this project.
Here is what I'm using to turn ARFrame.capturedImage into a UIImage, and subsequently, a video.
extension UIImage {
convenience init(pixelBuffer: CVPixelBuffer) {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let size = CGSize(width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
let tempContext = CIContext()
let image = tempContext.createCGImage(ciImage, from: CGRect(origin: CGPoint.zero, size: size))!
// This assumes we're using an iPhone in portrait.
self.init(cgImage: image, scale: 1, orientation: .right)
}
}
I actually found a library to do this. It's SceneKitVideoRecorder.
I don't fully understand how it works yet, but the important code is located in SceneKitVideoRecorder.swift.
You can try to use ReplayKit. I use ReplayKit in my AR App to record when I place a model to the scene and more.
Try this my snippet:
import ReplayKit
class YourController: UIViewController, RPPreviewViewControllerDelegate {
#IBAction func shotVideo(_ sender: UIButton) {
print("Video")
if !isRecording {
startRecording()
} else {
stopRecording()
}
}
func startRecording() {
guard recorder.isAvailable else {
print("The recording isn't available now.")
return
}
recorder.isMicrophoneEnabled = false
recorder.startRecording{ [unowned self] (error) in
guard error == nil else {
print("Trouble with starting this recording.")
return
}
print("Recording started with success.")
self.isRecording = true
}
}
func stopRecording() {
recorder.stopRecording { (preview, error) in
print("The recording is stopped")
guard preview != nil else {
print("The preview controller isn't available.")
return
}
let alert = UIAlertController(title: "End Recording", message: "Want to edit or delete this recording?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action: UIAlertAction) in
self.recorder.discardRecording(handler: { () -> Void in
print("Recording suffessfully deleted.")
})
})
let editAction = UIAlertAction(title: "Edit", style: .default, handler: { (action: UIAlertAction) -> Void in
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
})
alert.addAction(editAction)
alert.addAction(deleteAction)
self.present(alert, animated: true, completion: nil)
self.isRecording = false
}
}
// RPPreviewViewControllerDelegate
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
dismiss(animated: true)
}
}
This is my code, I hope I have been helpful :).

Retrieving MP4 Video File Stored in Photo Library

I'm trying to retrieve a video file I saved in the Photo Album in order to display it in a Table view controller. I have been looking for an answer but I can't seem to find one.
my function to save it to the Album looks like this:
#IBAction func saveToAlbum(_ sender: Any) {
UISaveVideoAtPathToSavedPhotosAlbum((completedMoviePath?.path)!, nil, nil, nil)
}
Seems like there is no function that could retrieve the video saved. Can someone please guide me into it?
use like this it's working for me fine in swift 3
let library = ALAssetsLibrary()
let outputURL = UserDefaults.standard.object(forKey: "url")
if library.videoAtPathIs(compatibleWithSavedPhotosAlbum: outputURL as! URL!) {
library.writeVideoAtPath(toSavedPhotosAlbum: outputURL as! URL!,completionBlock: { (assetURL:URL?, error:Error?) -> Void in
//writeVideoAtPath(toSavedPhotosAlbum: outputURL,completionBlock: { (assetURL:URL!, error:Error?) -> Void in
var title = ""
var message = ""
if error != nil {
title = "Error"
message = "Failed to save video"
} else {
title = "Success"
message = "Video saved"
}
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
})
}

Memory leak in app extension with UIAlertAction attached to UiViewController

I am developing an app extension that takes an url and upload it to a web service.
If there are errors in the upload request, an Alert should pop up and when the user dismisses it, the extension should complete.
Profiling this code with instruments show a memory leak with two NSISLinearexpression objects.
I found that the incriminating code is found in the UIAlertAction that dismisses the alert: without an action attached to the alert the leak disappear.
I'm supposing for some reason calling:
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
causes troubles with the dismiss of the UIAlertController.
Why is that happening?
Here is my code:
import UIKit
import Social
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
fetchStuff()
}
private func sendAlert(alertMessage:String) {
print("alerting")
let alert = UIAlertController(title: "Send video to Kodi", message: alertMessage, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.default) {
UIAlertAction in
print("Cancel Pressed")
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
}
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
private func fetchStuff() -> Void {
print("fetching")
guard let extensionItem = extensionContext?.inputItems[0] as? NSExtensionItem else {
print("Unable to get extensionItem")
return
} // check for only 1 attachment
let itemProvider = extensionItem.attachments as! [NSItemProvider]
let item = itemProvider.first
if (item?.hasItemConformingToTypeIdentifier("public.url"))! {
item?.loadItem(forTypeIdentifier: "public.url", options: nil, completionHandler:
{ [weak self] (item: NSSecureCoding?, error: Error?) -> Void in
if let url = item as? NSURL {
print(url.absoluteString!)
self?.sendAlert(alertMessage: "test")
}
})
}
else {
return
}
return
}
}
I just had a similar issue.
The cause of the issue for me was that the CoreData manager we built worked off the main dispatch queue. So when the core data manager called our completion block it was actually on a different queue. I added:
DispatchQueue.main.async { }
Around my dialog creation and present calls and the leak disappeared. Hope it helps :)

Uploading image to Firebase Storage

I'm using this simple code to upload the image to firebase storage.
let imageName = UUID().uuidString
let storageRef = FIRStorage.storage().reference().child("Devices_Images").child("\(imageName).png")
// let metaData = FIRStorageMetadata()
// metaData.contentType = "image/png"
if let uploadData = UIImagePNGRepresentation(self.ImageView.image!) {
storageRef.put(uploadData, metadata: nil, completion: { (data, error) in
if error != nil {
print(error)
} else {
print("Image Uploaded Succesfully")
let profileImageUrl = data?.downloadURL()?.absoluteString
}
I keep getting this error:
[Generic] Creating an image format with an unknown type is an error
Actually the conversion and image type are all png! so why I keep getting this error!
The image is uploaded from alpum or camera as here:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let theInfo:NSDictionary = info as NSDictionary
let img:UIImage = theInfo.object(forKey: UIImagePickerControllerOriginalImage) as! UIImage
ImageView.image = img
self.dismiss(animated: true, completion: nil)
}
#IBAction func AddPictureBtnAction(_ sender: AnyObject) {
// addPictureBtnAtion.enabled = false
let alertController : UIAlertController = UIAlertController(title: "أضف جهازًا", message: "التقط صورة من الكاميرا أو اختر من الألبوم", preferredStyle: .actionSheet)
let cameraAction : UIAlertAction = UIAlertAction(title: "صورة من الكاميرا", style: .default, handler: {(cameraAction) in
print("camera Selected...")
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) == true {
self.imagePicker.sourceType = .camera
self.present()
}else{
self.present(self.showAlert("عذرًا", Message: "الكاميرا ليست متاحة في هذا الجهاز أو تم منع الوصول لها!"), animated: true, completion: nil)
}
})
let libraryAction : UIAlertAction = UIAlertAction(title: "صورة من الألبوم", style: .default, handler: {(libraryAction) in
print("Photo library selected....")
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) == true {
self.imagePicker.sourceType = .photoLibrary
self.present()
}else{
self.present(self.showAlert("عذرًا", Message: "ألبوم الصور ليس متاحًا في هذا الجهاز أو تم منع الوصول له!"), animated: true, completion: nil)
}
})
let cancelAction : UIAlertAction = UIAlertAction(title: "إلغاء", style: .cancel , handler: {(cancelActn) in
print("Cancel action was pressed")
})
alertController.addAction(cameraAction)
alertController.addAction(libraryAction)
alertController.addAction(cancelAction)
alertController.popoverPresentationController?.sourceView = view
alertController.popoverPresentationController?.sourceRect = view.frame
self.present(alertController, animated: true, completion: nil)
}
func present(){
self.present(imagePicker, animated: true, completion: nil)
}
/* func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
print("info of the pic reached :\(info) ")
self.imagePicker.dismissViewControllerAnimated(true, completion: nil)
} */
//Show Alert
func showAlert(_ Title : String!, Message : String!) -> UIAlertController {
let alertController : UIAlertController = UIAlertController(title: Title, message: Message, preferredStyle: .alert)
let okAction : UIAlertAction = UIAlertAction(title: "Ok", style: .default) { (alert) in
print("User pressed ok function")
}
alertController.addAction(okAction)
alertController.popoverPresentationController?.sourceView = view
alertController.popoverPresentationController?.sourceRect = view.frame
return alertController
}
Add "_" in your parameter in your imagePicker method.
func imagePickerController(_ picker: UIImagePickerController ...
Then to upload:-
//if let uploadData = UIImagePNGRepresentation(UIImage(cgImage: self.imageView.image! as! CGImage, scale: 1.0, orientation: .up)) as? NSData {
if let uploadData = UIImagePNGRepresentation(self.ImageView.image!) as? NSData {
storageRef.put(uploadData!, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error)
} else {
print("Image Uploaded Succesfully")
let profileImageUrl = data?.downloadURL()?.absoluteString
}
})
}

Resources